From 1fd8c859f40c30f6357b7517bf9f9563492e6176 Mon Sep 17 00:00:00 2001 From: BuGu <1220627966@qq.com> Date: Fri, 17 Jan 2025 16:47:29 +0800 Subject: [PATCH] First Release --- kvm_system/.DS_Store | Bin 0 -> 6148 bytes kvm_system/README.md | 0 kvm_system/main/CMakeLists.txt | 28 + kvm_system/main/Kconfig | 0 kvm_system/main/include/config.h | 100 + kvm_system/main/lib/hdmi/hdmi.cpp | 238 ++ kvm_system/main/lib/hdmi/hdmi.h | 29 + kvm_system/main/lib/libqr/CMakeLists.txt | 59 + kvm_system/main/lib/libqr/LICENSE | 19 + kvm_system/main/lib/libqr/README | 1 + kvm_system/main/lib/libqr/TODO | 8 + kvm_system/main/lib/libqr/crc.h | 63 + kvm_system/main/lib/libqr/qr.c | 2611 +++++++++++++++++ kvm_system/main/lib/libqr/qr.h | 341 +++ kvm_system/main/lib/libqr/qr_dwtable.h | 48 + kvm_system/main/lib/libqr/qr_private.h | 512 ++++ kvm_system/main/lib/libqr/qr_util.h | 87 + kvm_system/main/lib/libqr/qrcmd.c | 626 ++++ kvm_system/main/lib/libqr/qrcmd.h | 110 + kvm_system/main/lib/libqr/qrcnv.c | 672 +++++ kvm_system/main/lib/libqr/qrcnv.h | 165 ++ kvm_system/main/lib/libqr/qrcnv_bmp.c | 324 ++ kvm_system/main/lib/oled_ctrl/oled_ctrl.cpp | 682 +++++ kvm_system/main/lib/oled_ctrl/oled_ctrl.h | 369 +++ kvm_system/main/lib/oled_ui/oled_ui.cpp | 506 ++++ kvm_system/main/lib/oled_ui/oled_ui.h | 11 + .../main/lib/system_ctrl/system_ctrl.cpp | 181 ++ kvm_system/main/lib/system_ctrl/system_ctrl.h | 16 + .../main/lib/system_init/system_init.cpp | 241 ++ kvm_system/main/lib/system_init/system_init.h | 10 + .../main/lib/system_state/system_state.cpp | 544 ++++ .../main/lib/system_state/system_state.h | 30 + kvm_system/main/src/main.cpp | 233 ++ kvmapp/.DS_Store | Bin 0 -> 6148 bytes kvmapp/jpg_stream/S95nanokvm | 91 + kvmapp/jpg_stream/jpg_stream | 5 + kvmapp/kvm_new_app | 0 kvmapp/kvm_system/kvm_stream | 0 kvmapp/kvm_system/kvm_system | Bin 0 -> 325048 bytes kvmapp/system/init.d/S01fs | 42 + kvmapp/system/init.d/S03usbdev | 128 + kvmapp/system/init.d/S15kvmhwd | 209 ++ kvmapp/system/init.d/S30eth | 115 + kvmapp/system/init.d/S30wifi | 164 ++ kvmapp/system/init.d/S50sshd | 61 + kvmapp/system/init.d/S95nanokvm | 50 + kvmapp/system/init.d/S98tailscaled | 113 + kvmapp/system/ko/soph_mipi_rx.ko | Bin 0 -> 125992 bytes kvmapp/system/update-nanokvm.py | 126 + 49 files changed, 9968 insertions(+) create mode 100755 kvm_system/.DS_Store create mode 100644 kvm_system/README.md create mode 100644 kvm_system/main/CMakeLists.txt create mode 100644 kvm_system/main/Kconfig create mode 100644 kvm_system/main/include/config.h create mode 100644 kvm_system/main/lib/hdmi/hdmi.cpp create mode 100644 kvm_system/main/lib/hdmi/hdmi.h create mode 100644 kvm_system/main/lib/libqr/CMakeLists.txt create mode 100644 kvm_system/main/lib/libqr/LICENSE create mode 100644 kvm_system/main/lib/libqr/README create mode 100644 kvm_system/main/lib/libqr/TODO create mode 100644 kvm_system/main/lib/libqr/crc.h create mode 100644 kvm_system/main/lib/libqr/qr.c create mode 100644 kvm_system/main/lib/libqr/qr.h create mode 100644 kvm_system/main/lib/libqr/qr_dwtable.h create mode 100644 kvm_system/main/lib/libqr/qr_private.h create mode 100644 kvm_system/main/lib/libqr/qr_util.h create mode 100644 kvm_system/main/lib/libqr/qrcmd.c create mode 100644 kvm_system/main/lib/libqr/qrcmd.h create mode 100644 kvm_system/main/lib/libqr/qrcnv.c create mode 100644 kvm_system/main/lib/libqr/qrcnv.h create mode 100644 kvm_system/main/lib/libqr/qrcnv_bmp.c create mode 100644 kvm_system/main/lib/oled_ctrl/oled_ctrl.cpp create mode 100644 kvm_system/main/lib/oled_ctrl/oled_ctrl.h create mode 100644 kvm_system/main/lib/oled_ui/oled_ui.cpp create mode 100644 kvm_system/main/lib/oled_ui/oled_ui.h create mode 100644 kvm_system/main/lib/system_ctrl/system_ctrl.cpp create mode 100644 kvm_system/main/lib/system_ctrl/system_ctrl.h create mode 100644 kvm_system/main/lib/system_init/system_init.cpp create mode 100644 kvm_system/main/lib/system_init/system_init.h create mode 100644 kvm_system/main/lib/system_state/system_state.cpp create mode 100644 kvm_system/main/lib/system_state/system_state.h create mode 100644 kvm_system/main/src/main.cpp create mode 100755 kvmapp/.DS_Store create mode 100755 kvmapp/jpg_stream/S95nanokvm create mode 100755 kvmapp/jpg_stream/jpg_stream create mode 100644 kvmapp/kvm_new_app create mode 100755 kvmapp/kvm_system/kvm_stream create mode 100755 kvmapp/kvm_system/kvm_system create mode 100755 kvmapp/system/init.d/S01fs create mode 100755 kvmapp/system/init.d/S03usbdev create mode 100755 kvmapp/system/init.d/S15kvmhwd create mode 100755 kvmapp/system/init.d/S30eth create mode 100755 kvmapp/system/init.d/S30wifi create mode 100755 kvmapp/system/init.d/S50sshd create mode 100755 kvmapp/system/init.d/S95nanokvm create mode 100755 kvmapp/system/init.d/S98tailscaled create mode 100644 kvmapp/system/ko/soph_mipi_rx.ko create mode 100755 kvmapp/system/update-nanokvm.py diff --git a/kvm_system/.DS_Store b/kvm_system/.DS_Store new file mode 100755 index 0000000000000000000000000000000000000000..bfe1b511dcc65b75f6eac4a63002c785c58855a9 GIT binary patch literal 6148 zcmeHK!EVz)5S>lkb`u2TfJ84yzHp1G1S%*8B$JjyC6JJe-~gz#u|+LhZxuTY5Q2Q? z3*aaC4!(eI;RJ7Xmnezp6(QJ>X5Z}WJUf2t?0Shv49CfUs7pi=&e&?B`Hk^7d&62b zvk4UX8p%PfUdFkK8?`R+cPhYhw@xvQDWjYg=Z{~Bd+_Za+Vs!AM0@m*B6>|JJpiMm z3cii@4OI(s&)hF7T#pjNSgsS-&Z~?J>%Y^s5*{q{8*`c9!K$$ES(&7uk5L!in1adX>yjND0%)gD-tyss&SE& z`Z}Q*kg_e?huy8|^mc!z=MQ#wXFY#<=SIKh@7%mKo3-WD>w~)oM<;LJP2bNxe9|(J zz~4dJWsASyGlIDgXW^*GRq+ZruJ`B!(q>ew2=ozSi<7Vc$x|B98MiC7-&bnUb>w^zx3@p|L_0oYx9|3?K(%KO7#~*Wii-E=3AVy%q zP=SUj>=8p4I`Y1)3oONy%UkglTpRK}2f)B$ TZ4eol`w-AFxW*~)M-})9_x6t& literal 0 HcmV?d00001 diff --git a/kvm_system/README.md b/kvm_system/README.md new file mode 100644 index 0000000..e69de29 diff --git a/kvm_system/main/CMakeLists.txt b/kvm_system/main/CMakeLists.txt new file mode 100644 index 0000000..939403d --- /dev/null +++ b/kvm_system/main/CMakeLists.txt @@ -0,0 +1,28 @@ + +list(APPEND ADD_INCLUDE + "include" + "lib/libqr" + "lib/system_ctrl" + "lib/system_state" + "lib/system_init" + "lib/oled_ctrl" + "lib/oled_ui" + "lib/hdmi" + ) + +list(APPEND ADD_PRIVATE_INCLUDE "") + +append_srcs_dir(ADD_SRCS + "src" + "lib/libqr" + "lib/system_ctrl" + "lib/system_state" + "lib/system_init" + "lib/oled_ctrl" + "lib/oled_ui" + "lib/hdmi" + ) # append source file in src dir to var ADD_SRCS + +list(APPEND ADD_REQUIREMENTS basic peripheral) + +register_component() diff --git a/kvm_system/main/Kconfig b/kvm_system/main/Kconfig new file mode 100644 index 0000000..e69de29 diff --git a/kvm_system/main/include/config.h b/kvm_system/main/include/config.h new file mode 100644 index 0000000..528c86d --- /dev/null +++ b/kvm_system/main/include/config.h @@ -0,0 +1,100 @@ +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#include "maix_basic.hpp" +#include "maix_time.hpp" +#include "maix_gpio.hpp" +#include "maix_pinmap.hpp" +#include "maix_i2c.hpp" +#include "maix_uart.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qr.h" +#include "system_ctrl.h" +#include "system_state.h" +#include "system_init.h" +#include "oled_ctrl.h" +#include "oled_ui.h" +#include "hdmi.h" + +#define IP_Change_time 5000 +#define QR_Change_time 5000 +#define STATE_DELAY 1000 +#define OLED_DELAY 1000 +#define KEY_DELAY 100 +#define KEY_LONG_PRESS 1500 +#define KEY_LONGLONG_PRESS 9000 +#define WIFI_CONNECTION_DELAY 5000 +#define OLED_SLEEP_DELAY_MIN 10 +#define OLED_SLEEP_DELAY_DEFAULT 30 + +typedef struct { + int8_t page = 0; + int8_t sub_page = 0; + uint8_t eth_route[16] = {0}; // route ip + uint8_t wifi_route[16] = {0}; // route ip + uint8_t eth_addr[16] = {0}; // ETH ip + uint8_t wifi_addr[16] = {0}; // WiFi ip + uint8_t tail_addr[16] = {0}; // Tailscale ip + uint8_t rndis_addr[16] = {0}; // RNDIS ip + int8_t eth_state = -1; // cat /sys/class/net/eth0/carrier + int8_t wifi_state = -1; // cat /sys/class/net/wlan0/carrier + int8_t tail_state = -1; // ifconfig tailscale0 | grep 'inet addr' | awk '{print $2}' + int8_t hdmi_state = -1; // cat /proc/cvitek/vi_dbg | grep VIFPS | awk '{print $3}' (1s) + int8_t usb_state = -1; // cat /sys/class/udc/4340000.usb/state + int8_t hid_state = -1; // (exist?) /sys/kernel/config/usb_gadget/g0/configs/c.1/hid.GSn(n=012) + int8_t rndis_state = -1; // (exist?) /sys/kernel/config/usb_gadget/g0/configs/c.1/rndis.usb0 + int8_t udisk_state = -1; // (exist?) /sys/kernel/config/usb_gadget/g0/configs/c.1/mass_storage.disk0 + int8_t host_pwr_state = -1; // cat /sys/class/gpio/gpio504/value + int16_t hdmi_width = 0; // cat /kvmapp/kvm/width + int16_t hdmi_height = 0; // cat /kvmapp/kvm/height + int8_t type = 0; // cat /kvmapp/kvm/type + int8_t now_fps = 0; // cat /kvmapp/kvm/now_fps + int16_t qlty = 0; // cat /kvmapp/kvm/qlty + int8_t oled_thread_running = 0; + int8_t key_thread_running = 0; + int8_t sys_thread_running = 0; + int8_t wifi_config_process = -1; // 1:QR;2:Test;3:IP; + char wifi_ap_pass[9] = {0}; + uint8_t oled_sleep_state = 0; // 0:wakeup; 1:sleep; + int8_t reconvery_update = 0; // 0:Undetected; 1:Needs Update; 2:Update finish; -1:not need to update +} kvm_sys_state_t; + +typedef struct { + int8_t page = -1; + int8_t sub_page = -1; + uint8_t eth_route[16] = {0}; // route ip + uint8_t wifi_route[16] = {0}; // route ip + uint8_t eth_addr[16] = {0}; // ETH ip + uint8_t wifi_addr[16] = {0}; // WiFi ip + uint8_t tail_addr[16] = {0}; // Tailscale ip + uint8_t rndis_addr[16] = {0}; // RNDIS ip + int8_t eth_state = -1; // cat /sys/class/net/eth0/carrier + int8_t wifi_state = -1; // cat /sys/class/net/wlan0/carrier + int8_t tail_state = -1; // ifconfig tailscale0 | grep inet\ addr | awk '{print $2}' + int8_t hdmi_state = -1; // cat /proc/cvitek/vi_dbg | grep VIFPS | awk '{print $3}' (1s) + int8_t usb_state = -1; // cat /sys/class/udc/4340000.usb/state + int8_t hid_state = -1; // (exist?) /sys/kernel/config/usb_gadget/g0/configs/c.1/hid.GSn(n=012) + int8_t rndis_state = -1; // (exist?) /sys/kernel/config/usb_gadget/g0/configs/c.1/rndis.usb0 + int8_t udisk_state = -1; // (exist?) /sys/kernel/config/usb_gadget/g0/configs/c.1/mass_storage.disk0 + int8_t host_pwr_state = -1; // cat /sys/class/gpio/gpio504/value + int16_t hdmi_width = -1; // cat /kvmapp/kvm/width + int16_t hdmi_height = -1; // cat /kvmapp/kvm/height + int8_t type = -1; // cat /kvmapp/kvm/type + int8_t now_fps = -1; // cat /kvmapp/kvm/now_fps + int16_t qlty = -1; // cat /kvmapp/kvm/qlty + uint8_t oled_sleep_param = 0; + uint8_t oled_sleep_state = 0; // 0:wakeup; 1:sleep; + uint64_t oled_sleep_start = 0; +} kvm_oled_state_t; + +#endif // CONFIG_H_ diff --git a/kvm_system/main/lib/hdmi/hdmi.cpp b/kvm_system/main/lib/hdmi/hdmi.cpp new file mode 100644 index 0000000..fd10587 --- /dev/null +++ b/kvm_system/main/lib/hdmi/hdmi.cpp @@ -0,0 +1,238 @@ +#include "hdmi.h" + +using namespace maix; +using namespace maix::sys; +using namespace maix::peripheral; +i2c::I2C LT6911_i2c(4, i2c::Mode::MASTER); + +void lt6911_enable() +{ + uint8_t buf[2]; + buf[0] = 0xff; + buf[1] = 0x80; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + buf[0] = 0xee; + buf[1] = 0x01; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); +} + +void lt6911_disable() +{ + uint8_t buf[2]; + buf[0] = 0xff; + buf[1] = 0x80; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + buf[0] = 0xee; + buf[1] = 0x00; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); +} + +void lt6911_start() +{ + uint8_t buf[2]; + + buf[0] = 0xff; + buf[1] = 0x80; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + buf[0] = 0x5A; + buf[1] = 0x80; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); +} + +void lt6911_stop() +{ + uint8_t buf[2]; + + buf[0] = 0xff; + buf[1] = 0x80; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + buf[0] = 0x5A; + buf[1] = 0x88; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); +} + +void lt6911_reset() +{ + lt6911_stop(); + time::sleep_ms(1); + lt6911_start(); +} + +void lt6911_get_hdmi_errer() +{ + uint8_t buf[6]; + + buf[0] = 0xff; + buf[1] = 0xC0; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + buf[0] = 0x20; + buf[1] = 0x01; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + time::sleep_ms(100); + + buf[0] = 0x24; + LT6911_i2c.writeto(LT6911_ADDR, buf, 1); + + maix::Bytes *dat = LT6911_i2c.readfrom(LT6911_ADDR, 6); + + buf[0] = 0x20; + buf[1] = 0x07; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + for(int i = 0; i < 6; i++){ + buf[i] = (uint8_t)dat->data[i]; + } + delete dat; + + printf("hdmi_errer_code = %x, %x, %x, %x, %x, %x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); +} + +uint8_t lt6911_get_hdmi_res() +{ + uint8_t buf[2]; + uint8_t revbuf[4]; + uint16_t Vactive; + uint16_t Hactive; + + buf[0] = 0xff; + buf[1] = 0xd2; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + buf[0] = 0x83; + buf[1] = 0x11; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + time::sleep_ms(100); + + // Vactive + buf[0] = 0x96; + LT6911_i2c.writeto(LT6911_ADDR, buf, 1); + maix::Bytes *dat0 = LT6911_i2c.readfrom(LT6911_ADDR, 2); + + // Hactive + buf[0] = 0x8b; + LT6911_i2c.writeto(LT6911_ADDR, buf, 1); + maix::Bytes *dat1 = LT6911_i2c.readfrom(LT6911_ADDR, 2); + + revbuf[0] = (uint8_t)dat0->data[0]; + revbuf[1] = (uint8_t)dat0->data[1]; + revbuf[2] = (uint8_t)dat1->data[0]; + revbuf[3] = (uint8_t)dat1->data[1]; + + Vactive = (revbuf[0] << 8)|revbuf[1]; + Hactive = (revbuf[2] << 8)|revbuf[3]; + Hactive *= 2; + + printf("HDMI res modification event\n"); + printf("new res: %d * %d\n", Hactive, Vactive); + + delete dat0; + delete dat1; + + if (Vactive != 0 && Hactive != 0){ + return 1; + } else { + return 0; + } +} + +void lt6911_get_hdmi_clk() +{ + uint8_t buf[2]; + uint8_t revbuf[3]; + uint32_t clk; + + buf[0] = 0xff; + buf[1] = 0xa0; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + buf[0] = 0x34; + buf[1] = 0x0b; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + time::sleep_ms(50); + + // clk + buf[0] = 0xff; + buf[1] = 0xb8; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + buf[0] = 0xb1; + LT6911_i2c.writeto(LT6911_ADDR, buf, 1); + maix::Bytes *dat0 = LT6911_i2c.readfrom(LT6911_ADDR, 3); + + revbuf[0] = (uint8_t)dat0->data[0]; + revbuf[1] = (uint8_t)dat0->data[1]; + revbuf[2] = (uint8_t)dat0->data[2]; + revbuf[0] &= 0x07; + + clk = revbuf[0]; + clk <<= 8; + clk |= revbuf[1]; + clk <<= 8; + clk |= revbuf[2]; + + printf("HDMI CLK = %d\n", clk); + + delete dat0; +} + +uint8_t lt6911_get_csi_res() +{ + uint8_t ret = 0; + uint8_t buf[2]; + uint8_t revbuf[4]; + static uint16_t old_Vactive; + static uint16_t old_Hactive; + uint16_t Vactive; + uint16_t Hactive; + char Cmd[100]={0}; + + buf[0] = 0xff; + buf[1] = 0xc2; + LT6911_i2c.writeto(LT6911_ADDR, buf, 2); + + // Vactive + buf[0] = 0x06; + LT6911_i2c.writeto(LT6911_ADDR, buf, 1); + maix::Bytes *dat0 = LT6911_i2c.readfrom(LT6911_ADDR, 2); + + // Hactive + buf[0] = 0x38; + LT6911_i2c.writeto(LT6911_ADDR, buf, 1); + maix::Bytes *dat1 = LT6911_i2c.readfrom(LT6911_ADDR, 2); + + revbuf[0] = (uint8_t)dat0->data[0]; + revbuf[1] = (uint8_t)dat0->data[1]; + revbuf[2] = (uint8_t)dat1->data[0]; + revbuf[3] = (uint8_t)dat1->data[1]; + + Vactive = (revbuf[0] << 8)|revbuf[1]; + Hactive = (revbuf[2] << 8)|revbuf[3]; + + if(old_Hactive != Hactive || old_Vactive != Vactive){ + old_Hactive = Hactive; + old_Vactive = Vactive; + ret = 1; + } + + printf("CSI res: %d * %d\n", Hactive, Vactive); + // setenv("KVM_CSI_HEIGHT", to_string(Hactive).c_str(), 1); + // setenv("KVM_CSI_WIDTH", to_string(Vactive).c_str(), 1); + + sprintf(Cmd, "echo %d > /kvmapp/kvm/width", Hactive); + system(Cmd); + sprintf(Cmd, "echo %d > /kvmapp/kvm/height", Vactive); + system(Cmd); + + delete dat0; + delete dat1; + + return ret; +} \ No newline at end of file diff --git a/kvm_system/main/lib/hdmi/hdmi.h b/kvm_system/main/lib/hdmi/hdmi.h new file mode 100644 index 0000000..cddb660 --- /dev/null +++ b/kvm_system/main/lib/hdmi/hdmi.h @@ -0,0 +1,29 @@ +#ifndef HDMI_H_ +#define HDMI_H_ + +#include "maix_basic.hpp" +#include "maix_time.hpp" +#include "maix_gpio.hpp" +#include "maix_pinmap.hpp" +#include "maix_i2c.hpp" +#include +#include +#include +#include +#include + +#define LT6911_ADDR 0x2B +#define LT6911_READ 0xFF +#define LT6911_WRITE 0x00 + +void lt6911_enable(); +void lt6911_disable(); +void lt6911_start(); +void lt6911_stop(); +void lt6911_reset(); +void lt6911_get_hdmi_errer(); +uint8_t lt6911_get_hdmi_res(); +void lt6911_get_hdmi_clk(); +uint8_t lt6911_get_csi_res(); + +#endif // HDMI_H_ \ No newline at end of file diff --git a/kvm_system/main/lib/libqr/CMakeLists.txt b/kvm_system/main/lib/libqr/CMakeLists.txt new file mode 100644 index 0000000..b60d6fa --- /dev/null +++ b/kvm_system/main/lib/libqr/CMakeLists.txt @@ -0,0 +1,59 @@ +project(qr) +cmake_minimum_required(VERSION 2.6.0) + +set(QR_VERSION "1.0.0") +set(QR_SOVERSION "1") + +set(QR_COMMAND_SOURCES qrcmd.c) +set(QR_LIBRARY_SOURCES + qr.c qrcnv.c qrcnv_bmp.c qrcnv_png.c qrcnv_svg.c qrcnv_tiff.c +) +set(QR_PUBLIC_HEADERS qr.h) + +set(bindir bin) +set(incdir include) +set(libdir lib) + +set(CMAKE_SKIP_BUILD_RPATH OFF) +set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF) +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${libdir}") +set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${libdir}") + +find_package(ZLIB) + +add_definitions(-Wall -Wextra) + +include_directories(${ZLIB_INCLUDE_DIRS}) + +add_executable(qrcmd ${QR_COMMAND_SOURCES}) +add_executable(qrcmd_multi ${QR_COMMAND_SOURCES}) + +add_library(libqr_shared SHARED ${QR_LIBRARY_SOURCES}) +add_library(libqr_static STATIC ${QR_LIBRARY_SOURCES}) + +target_link_libraries(qrcmd libqr_shared) +target_link_libraries(qrcmd_multi libqr_shared) +target_link_libraries(libqr_shared m ${ZLIB_LIBRARIES}) + +set_target_properties(qrcmd PROPERTIES + OUTPUT_NAME qr +) +set_target_properties(qrcmd_multi PROPERTIES + OUTPUT_NAME qrs + COMPILE_FLAGS -DQRCMD_STRUCTURED_APPEND +) +set_target_properties(libqr_shared PROPERTIES + OUTPUT_NAME qr + VERSION ${QR_VERSION} + SOVERSION ${QR_SOVERSION} +) +set_target_properties(libqr_static PROPERTIES + OUTPUT_NAME qr +) + +install(TARGETS qrcmd qrcmd_multi libqr_shared libqr_static + RUNTIME DESTINATION ${bindir} + LIBRARY DESTINATION ${libdir} + ARCHIVE DESTINATION ${libdir} +) +install(FILES ${QR_PUBLIC_HEADERS} DESTINATION ${incdir}) diff --git a/kvm_system/main/lib/libqr/LICENSE b/kvm_system/main/lib/libqr/LICENSE new file mode 100644 index 0000000..85a58e3 --- /dev/null +++ b/kvm_system/main/lib/libqr/LICENSE @@ -0,0 +1,19 @@ +All rights reserved. + +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. diff --git a/kvm_system/main/lib/libqr/README b/kvm_system/main/lib/libqr/README new file mode 100644 index 0000000..17700bc --- /dev/null +++ b/kvm_system/main/lib/libqr/README @@ -0,0 +1 @@ +This is a C library and a command line tool to make a QR Code. diff --git a/kvm_system/main/lib/libqr/TODO b/kvm_system/main/lib/libqr/TODO new file mode 100644 index 0000000..c649aa2 --- /dev/null +++ b/kvm_system/main/lib/libqr/TODO @@ -0,0 +1,8 @@ +TODO: +- test on Win32 +- test on Cygwin (if I could) +- write tests +- write manpage of qr(1), qrs(1) and libqr(3) +- localization support by ICU4C +- pkg-config (.pc file) support +- rcfile support for command line tool diff --git a/kvm_system/main/lib/libqr/crc.h b/kvm_system/main/lib/libqr/crc.h new file mode 100644 index 0000000..e1983b7 --- /dev/null +++ b/kvm_system/main/lib/libqr/crc.h @@ -0,0 +1,63 @@ +/* + * This code is taken from: + * PNG (Portable Network Graphics) Specification, Version 1.1 + * 15. Appendix: Sample CRC Code + * http://www.libpng.org/pub/png/spec/1.1/PNG-CRCAppendix.html + */ + +#ifdef uint32_t +typedef uint32_t crc_t; +#else +typedef unsigned long crc_t; +#endif + +/* Table of CRCs of all 8-bit messages. */ +static crc_t crc_table[256]; + +/* Flag: has the table been computed? Initially false. */ +static int crc_table_computed = 0; + +/* Make the table for a fast CRC. */ +static void make_crc_table(void) +{ + crc_t c; + int n, k; + + for (n = 0; n < 256; n++) { + c = (crc_t) n; + for (k = 0; k < 8; k++) { + if (c & 1) { + c = 0xedb88320L ^ (c >> 1); + } else { + c = c >> 1; + } + } + crc_table[n] = c; + } + crc_table_computed = 1; +} + +/* Update a running CRC with the bytes buf[0..len-1]--the CRC + should be initialized to all 1's, and the transmitted value + is the 1's complement of the final running CRC (see the + crc() routine below)). */ + +static crc_t update_crc(crc_t crc, const unsigned char *buf, int len) +{ + crc_t c = crc; + int n; + + if (!crc_table_computed) { + make_crc_table(); + } + for (n = 0; n < len; n++) { + c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); + } + return c; +} + +/* Return the CRC of the bytes buf[0..len-1]. */ +static crc_t crc(const unsigned char *buf, int len) +{ + return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL; +} diff --git a/kvm_system/main/lib/libqr/qr.c b/kvm_system/main/lib/libqr/qr.c new file mode 100644 index 0000000..07ca444 --- /dev/null +++ b/kvm_system/main/lib/libqr/qr.c @@ -0,0 +1,2611 @@ +/* + * QR Code Generator Library + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "qr.h" +#include "qr_util.h" +#include "qr_private.h" +#include "qr_dwtable.h" + +#define qrIsData(qr, i, j) (((qr)->symbol[i][j] & QR_MM_DATA) != 0) +#define qrIsFunc(qr, i, j) (((qr)->symbol[i][j] & QR_MM_FUNC) != 0) + +QR_API const char *(*qrGetCurrentFunctionName)(void) = NULL; + +/* + * ライブラリのバージョンを返す + */ +QR_API const char * +qrVersion(void) +{ + return LIBQR_VERSION; +} + +/* + * QRCodeオブジェクトを生成する + */ +QR_API QRCode * +qrInit(int version, int mode, int eclevel, int masktype, int *errcode) +{ + QRCode *qr = NULL; + + /* + * メモリを確保する + */ + qr = (QRCode *)calloc(1, sizeof(QRCode)); + if (qr == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + return NULL; + } + qr->dataword = (qr_byte_t *)calloc(1, QR_DWD_MAX); + qr->ecword = (qr_byte_t *)calloc(1, QR_ECW_MAX); + qr->codeword = (qr_byte_t *)calloc(1, QR_CWD_MAX); + if (qr->dataword == NULL || qr->ecword == NULL || qr->codeword == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + qrDestroy(qr); + return NULL; + } + + /* + * 内部状態を初期化する + */ + qr->_symbol = NULL; + qr->symbol = NULL; + qr->source = NULL; + qr->srcmax = 0; + qr->srclen = 0; + qr->enclen = 0; + qr->delta1 = 0; + qr->delta2 = 0; + qr->errcode = QR_ERR_NONE; + qr->state = QR_STATE_BEGIN; + + /* + * 型番を設定する + */ + if (version == -1 || (version >= 1 && version <= QR_VER_MAX)) { + qr->param.version = version; + } else { + *errcode = QR_ERR_INVALID_VERSION; + qrDestroy(qr); + return NULL; + } + + /* + * 符号化モードを設定する + */ + if (mode == QR_EM_AUTO || (mode >= QR_EM_NUMERIC && mode < QR_EM_COUNT)) { + qr->param.mode = mode; + } else { + *errcode = QR_ERR_INVALID_MODE; + qrDestroy(qr); + return NULL; + } + + /* + * 誤り訂正レベルを設定する + */ + if (eclevel >= QR_ECL_L && eclevel < QR_EM_COUNT) { + qr->param.eclevel = eclevel; + } else { + *errcode = QR_ERR_INVALID_ECL; + qrDestroy(qr); + return NULL; + } + + /* + * マスクパターンを設定する + */ + if (masktype == -1 || (masktype >= 0 && masktype < QR_MPT_MAX)) { + qr->param.masktype = masktype; + } else { + *errcode = QR_ERR_INVALID_MPT; + qrDestroy(qr); + return NULL; + } + + return qr; +} + +/* + * QRStructuredオブジェクトを生成する + */ +QR_API QRStructured * +qrsInit(int version, int mode, int eclevel, int masktype, int maxnum, int *errcode) +{ + QRStructured *st = NULL; + + /* + * メモリを確保する + */ + st = (QRStructured *)calloc(1, sizeof(QRStructured)); + if (st == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + return NULL; + } + + /* + * 内部状態を初期化する + */ + st->parity = 0; + st->state = QR_STATE_BEGIN; + + /* + * 最大シンボル数を設定する + */ + if (maxnum >= 2 && masktype <= QR_STA_MAX) { + st->max = maxnum; + } else { + *errcode = QR_ERR_INVALID_MAXNUM; + qrsDestroy(st); + return NULL; + } + + /* + * 型番を設定する + */ + if (version >= 1 && version <= QR_VER_MAX) { + st->param.version = version; + } else { + *errcode = QR_ERR_INVALID_VERSION; + qrsDestroy(st); + return NULL; + } + + /* + * 符号化モードを設定する + */ + if (mode == QR_EM_AUTO || (mode >= QR_EM_NUMERIC && mode < QR_EM_COUNT)) { + st->param.mode = mode; + } else { + *errcode = QR_ERR_INVALID_MODE; + qrsDestroy(st); + return NULL; + } + + /* + * 誤り訂正レベルを設定する + */ + if (eclevel >= QR_ECL_L && eclevel < QR_EM_COUNT) { + st->param.eclevel = eclevel; + } else { + *errcode = QR_ERR_INVALID_ECL; + qrsDestroy(st); + return NULL; + } + + /* + * マスクパターンを設定する + */ + if (masktype == -1 || (masktype >= 0 && masktype < QR_MPT_MAX)) { + st->param.masktype = masktype; + } else { + *errcode = QR_ERR_INVALID_MPT; + qrsDestroy(st); + return NULL; + } + + /* + * 一つめのQRコードオブジェクトを初期化する + */ + st->qrs[0] = qrInit(st->param.version, st->param.mode, + st->param.eclevel, st->param.masktype, errcode); + if (st->qrs[0] == NULL) { + qrsDestroy(st); + return NULL; + } + st->cur = st->qrs[0]; + st->num = 1; + + return st; +} + +/* + * QRCodeオブジェクトを複製する + */ +QR_API QRCode * +qrClone(const QRCode *qr, int *errcode) +{ + QRCode *cp = NULL; + + /* + * QRCodeオブジェクト用のメモリを確保し、複製する + */ + cp = (QRCode *)malloc(sizeof(QRCode)); + if (cp == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + return NULL; + } + memcpy(cp, qr, sizeof(QRCode)); + + /* + * 動的に確保されるメンバをいったんNULLにする + */ + cp->dataword = NULL; + cp->ecword = NULL; + cp->codeword = NULL; + cp->_symbol = NULL; + cp->symbol = NULL; + cp->source = NULL; + + /* + * ファイナライズ後ならシンボル、ファイナライズ前なら計算用領域を複製 + */ + if (cp->state == QR_STATE_FINAL) { + int i, dim; + + dim = qr_vertable[cp->param.version].dimension; + + cp->_symbol = (qr_byte_t *)calloc((size_t)dim, (size_t)dim); + if (cp->_symbol == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + qrDestroy(cp); + return NULL; + } + memcpy(cp->_symbol, qr->_symbol, (size_t)(dim * dim)); + + cp->symbol = (qr_byte_t **)malloc(sizeof(qr_byte_t *) * (size_t)dim); + if (cp->symbol == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + qrDestroy(cp); + return NULL; + } + for (i = 0; i < dim; i++) { + cp->symbol[i] = cp->_symbol + dim * i; + } + } else { + cp->dataword = (qr_byte_t *)malloc(QR_DWD_MAX); + cp->ecword = (qr_byte_t *)malloc(QR_ECW_MAX); + cp->codeword = (qr_byte_t *)malloc(QR_CWD_MAX); + if (cp->dataword == NULL || cp->ecword == NULL || cp->codeword == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + qrDestroy(cp); + return NULL; + } + memcpy(cp->dataword, qr->dataword, QR_DWD_MAX); + memcpy(cp->ecword , qr->ecword , QR_ECW_MAX); + memcpy(cp->codeword, qr->codeword, QR_CWD_MAX); + } + + /* + * 入力データを複製 + */ + if (cp->srcmax > 0 && qr->source != NULL) { + cp->source = (qr_byte_t *)malloc(cp->srcmax); + if (cp->source == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + qrDestroy(cp); + return NULL; + } + memcpy(cp->source, qr->source, cp->srclen); + } + + return cp; +} + +/* + * QRStructuredオブジェクトを複製する + */ +QR_API QRStructured * +qrsClone(const QRStructured *st, int *errcode) +{ + QRStructured *cps = NULL; + int i = 0; + + /* + * QRStructuredオブジェクト用のメモリを確保し、複製する + */ + cps = (QRStructured *)malloc(sizeof(QRStructured)); + if (cps == NULL) { + *errcode = QR_ERR_MEMORY_EXHAUSTED; + return NULL; + } + memcpy(cps, st, sizeof(QRStructured)); + + /* + * 保持しているQRCodeオブジェクトを複製する + */ + while (i < cps->num) { + QRCode *cp = qrClone(st->qrs[i], errcode); + if (cp == NULL) { + while (i > 0) { + qrDestroy(cps->qrs[--i]); + free(cps); + } + return NULL; + } + cps->qrs[i++] = cp; + } + while (i < QR_STA_MAX) { + cps->qrs[i++] = NULL; + } + cps->cur = cps->qrs[0] + (st->cur - st->qrs[0]); + + return cps; +} + +/* + * QRCodeオブジェクトを開放する + */ +QR_API void +qrDestroy(QRCode *qr) +{ + if (qr == NULL) { + return; + } + qrFree(qr->source); + qrFree(qr->dataword); + qrFree(qr->ecword); + qrFree(qr->codeword); + qrFree(qr->symbol); + qrFree(qr->_symbol); + free(qr); +} + +/* + * QRStructuredオブジェクトを開放する + */ +QR_API void +qrsDestroy(QRStructured *st) +{ + int i; + if (st == NULL) { + return; + } + for (i = 0; i < st->num; i++) { + qrDestroy(st->qrs[i]); + } + free(st); +} + +/* + * 出力形式に対応するMIMEタイプを返す + */ +QR_API const char * +qrMimeType(int format) +{ + switch (format) { + case QR_FMT_PNG: return "image/png"; + case QR_FMT_BMP: return "image/bmp"; + case QR_FMT_TIFF: return "image/tiff"; + case QR_FMT_PBM: return "image/x-portable-bitmap"; + case QR_FMT_SVG: return "image/svg+xml"; + case QR_FMT_JSON: return "application/json"; + case QR_FMT_DIGIT: return "text/plain"; + case QR_FMT_ASCII: return "text/plain"; + default: return NULL; + } +} + +/* + * 出力形式に対応する拡張子 (ピリオドなし) を返す + */ +QR_API const char * +qrExtension(int format) +{ + switch (format) { + case QR_FMT_PNG: return "png"; + case QR_FMT_BMP: return "bmp"; + case QR_FMT_TIFF: return "tiff"; + case QR_FMT_PBM: return "pbm"; + case QR_FMT_SVG: return "svg"; + case QR_FMT_JSON: return "json"; + case QR_FMT_DIGIT: return "txt"; + case QR_FMT_ASCII: return "txt"; + default: return NULL; + } +} + +/* + * QRコードオブジェクトに登録されているエラー番号を返す + */ +QR_API int +qrGetErrorCode(QRCode *qr) +{ + return qr->errcode; +} + +/* + * QRコードオブジェクトに登録されているエラー情報を返す + */ +QR_API char * +qrGetErrorInfo(QRCode *qr) +{ + return &(qr->errinfo[0]); +} + +/* + * 構造的連接の最後のQRコードオブジェクトに登録されているエラー番号を返す + */ +QR_API int +qrsGetErrorCode(QRStructured *st) +{ + return st->cur->errcode; +} + +/* + * 構造的連接の最後のQRコードオブジェクトに登録されているエラー情報を返す + */ +QR_API char * +qrsGetErrorInfo(QRStructured *st) +{ + return &(st->cur->errinfo[0]); +} + +/* + * エラー番号に対応したエラー情報を返す + */ +QR_API const char * +qrStrError(int errcode) +{ + switch (errcode) { + /* wide use errors */ + case QR_ERR_NONE: + case QR_ERR_USAGE: + return ""; + + case QR_ERR_SEE_ERRNO: + return "For more information, check for errno"; + + case QR_ERR_NOT_IMPL: + return "Not yet implemented"; + + case QR_ERR_STATE: + return "Not allowed in the current state"; + + case QR_ERR_FOPEN: + return "Failed to open file"; + + case QR_ERR_FREAD: + return "Failed to read data"; + + case QR_ERR_FWRITE: + return "Failed to write data"; + + case QR_ERR_MEMORY_EXHAUSTED: + return "Memory exhausted"; + + /* invalid parameter errors */ + case QR_ERR_INVALID_ARG: + return "Invalid argument"; + + case QR_ERR_INVALID_VERSION: + return "Invalid version number"; + + case QR_ERR_INVALID_MODE: + return "Invalid encoding mode"; + + case QR_ERR_INVALID_ECL: + return "Invalid error correction level"; + + case QR_ERR_INVALID_MPT: + return "Invalid mask pattern type"; + + case QR_ERR_INVALID_MAG: + return "Invalid pixel magnifying ratio"; + + case QR_ERR_INVALID_SEP: + return "Invalid separator width"; + + case QR_ERR_INVALID_SIZE: + return "Invalid output size"; + + case QR_ERR_INVALID_FMT: + return "Invalid output format"; + + case QR_ERR_INVALID_OUT: + return "Invalid output pathname"; + + case QR_ERR_INVALID_MAXNUM: + return "Invalid maximum symbol number"; + + case QR_ERR_UNSUPPORTED_FMT: + return "Unsupported output format"; + + case QR_ERR_EMPTY_PARAM: + return "Parameter required"; + + /* input data size/type errors */ + case QR_ERR_EMPTY_SRC: + return "Input data is empty"; + + case QR_ERR_LARGE_SRC: + return "Input data too large"; + + case QR_ERR_NOT_NUMERIC: + return "Non decimal characters found"; + + case QR_ERR_NOT_ALNUM: + return "Non alphanumeric characters found"; + + case QR_ERR_NOT_KANJI: + return "Non JIS X 0208 kanji sequence found"; + + /* imaging related errors */ + case QR_ERR_IMAGE_TOO_LARGE: + return "Output image size too large"; + + case QR_ERR_WIDTH_TOO_LARGE: + return "Output image width too large"; + + case QR_ERR_HEIGHT_TOO_LARGE: + return "Output image height too large"; + + case QR_ERR_IMAGECREATE: + return "Failed to create image"; + + case QR_ERR_IMAGEFORMAT: + return "Failed to convert image"; + + case QR_ERR_IMAGEFRAME: + return "Failed to create frame"; + + /* zlib related errors */ + case QR_ERR_DEFLATE: + return "Failed to deflate"; + + /* unknown error(s) */ + case QR_ERR_UNKNOWN: + default: + return "Unknown error"; + } +} + +/* + * libqrのエラー番号からエラー情報を設定する + */ +QR_API void +qrSetErrorInfo(QRCode *qr, int errnum, const char *param) +{ + qr->errcode = errnum; + if (param != NULL) { + snprintf(&(qr->errinfo[0]), QR_ERR_MAX, "%s: %s", param, qrStrError(errnum)); + } else { + snprintf(&(qr->errinfo[0]), QR_ERR_MAX, "%s", qrStrError(errnum)); + } +} + +/* + * システム標準のエラー番号からエラー情報を設定する + */ +QR_API void +qrSetErrorInfo2(QRCode *qr, int errnum, const char *param) +{ + char *info; + int size = 0; + info = &(qr->errinfo[0]); + qr->errcode = QR_ERR_SEE_ERRNO; + if (param != NULL) { + size = snprintf(info, QR_ERR_MAX, "%s: ", param); + if (size < 0 || size >= QR_ERR_MAX) { + return; + } + info += size; + } +#ifdef WIN32 + snprintf(info, (size_t)(QR_ERR_MAX - size), "%s", strerror(errnum)); +#else + strerror_r(errnum, info, (size_t)(QR_ERR_MAX - size)); +#endif +} + +/* + * libqrのエラー番号と可変長パラメータからエラー情報を設定する + */ +QR_API void +qrSetErrorInfo3(QRCode *qr, int errnum, const char *fmt, ...) +{ + char info[QR_ERR_MAX]; + va_list ap; + + qr->errcode = errnum; + va_start(ap, fmt); + vsnprintf(&(info[0]), QR_ERR_MAX, fmt, ap); + va_end(ap); + snprintf(&(qr->errinfo[0]), QR_ERR_MAX, "%s%s", qrStrError(errnum), info); +} + +/* + * 最適な符号化方法を調べる + */ +QR_API int +qrDetectDataType(const qr_byte_t *source, int size) +{ + if (qrStrPosNotNumeric(source, size) == -1) { + return QR_EM_NUMERIC; + } + if (qrStrPosNotAlnum(source, size) == -1) { + return QR_EM_ALNUM; + } + if (qrStrPosNotKanji(source, size) == -1) { + return QR_EM_KANJI; + } + return QR_EM_8BIT; +} + +/* + * 数字以外のデータが現れる位置を調べる + */ +QR_API int +qrStrPosNotNumeric(const qr_byte_t *source, int size) +{ + int p = 0; + + while (p < size) { + if (source[p] < '0' || source[p] > '9') { + return p; + } + p++; + } + return -1; +} + +/* + * 英数字以外のデータが現れる位置を調べる + */ +QR_API int +qrStrPosNotAlnum(const qr_byte_t *source, int size) +{ + int p = 0; + + while (p < size) { + if (qr_alnumtable[source[p]] == -1) { + return p; + } + p++; + } + return -1; +} + +/* + * JIS X 0208漢字以外のデータが現れる位置を調べる + */ +QR_API int +qrStrPosNotKanji(const qr_byte_t *source, int size) +{ + qr_byte_t x, y; + int p = 0; + + while (p < size - 1) { + x = source[p++]; + if (x >= 0x81 && x <= 0x9f) { + x -= 0x81; + } else if (x >= 0xe0 && x <= 0xea) { + x -= 0xc1; + } else { + /* JIS X 0208漢字の1バイトめでない */ + return p - 1; + } + y = source[p++]; + if (y >= 0x40 && y <= 0xfc) { + y -= 0x40; + } else { + /* JIS X 0208漢字の2バイトめでない */ + return p - 1; + } + if (qr_dwtable_kanji[x][y] == -1) { + /* JIS X 0208漢字の未定義領域 */ + return p - 2; + } + } + if (p < size) { + return p; + } + return -1; +} + +/* + * 英数字もしくはJIS X 0208漢字のデータが現れる位置を調べる + */ +QR_API int +qrStrPosNot8bit(const qr_byte_t *source, int size) +{ + qr_byte_t x, y; + int p = 0; + + while (p < size) { + x = source[p++]; + if (qr_alnumtable[x] != -1) { + return p - 1; + } + if (p < size && ((x >= 0x81 && x <= 0x9f) || (x >= 0xe0 && x <= 0xea))) { + if (x < 0xa0) { + x -= 0x81; + } else { + x -= 0xc1; + } + y = source[p]; + if (y >= 0x40 && y <= 0xfc && qr_dwtable_kanji[x][y - 0x40] != -1) { + return p - 1; + } + } + } + return -1; +} + +/* + * デフォルトの符号化モードでsizeバイト符号化したときのビット長を返す + */ +QR_API int +qrGetEncodedLength(QRCode *qr, int size) +{ + return qrGetEncodedLength2(qr, size, qr->param.mode); +} + +/* + * 特定の符号化モードでsizeバイト符号化したときのビット長を返す + */ +QR_API int +qrGetEncodedLength2(QRCode *qr, int size, int mode) +{ + int n, v; + + /* + * モード指示子と文字数指示子のサイズ + */ + v = (qr->param.version == -1) ? QR_VER_MAX : qr->param.version; + n = 4 + qr_vertable[v].nlen[mode]; + + /* + * 符号化モードごとのデータサイズ + */ + switch (mode) { + case QR_EM_NUMERIC: + /* + * 数字モード: 3桁ごとに10ビット + * (余りは1桁なら4ビット, 2桁なら7ビット) + */ + n += (size / 3) * 10; + switch (size % 3) { + case 1: + n += 4; + break; + case 2: + n += 7; + break; + } + break; + case QR_EM_ALNUM: + /* + * 英数字モード: 2桁ごとに11ビット + * (余りは1桁につき6ビット) + */ + n += (size / 2) * 11; + if (size % 2 == 1) { + n += 6; + } + break; + case QR_EM_8BIT: + /* + * 8ビットバイトモード: 1桁ごとに8ビット + */ + n += size * 8; + break; + case QR_EM_KANJI: + /* + * 漢字モード: 1文字(2バイト)ごとに13ビット + */ + n += (size / 2) * 13; + break; + default: + qrSetErrorInfo(qr, QR_ERR_INVALID_MODE, NULL); + return -1; + } + + return n; +} + +/* + * デフォルトの符号化モードでsizeビットを上限として符号化可能な最大のバイト長を返す + */ +QR_API int +qrGetEncodableLength(QRCode *qr, int size) +{ + return qrGetEncodableLength2(qr, size, qr->param.mode); +} + +/* + * 特定の符号化モードでsizeビットを上限として符号化可能な最大のバイト長を返す + */ +QR_API int +qrGetEncodableLength2(QRCode *qr, int size, int mode) +{ + int l, m, n, v; + + /* + * モード指示子と文字数指示子のサイズ + */ + v = (qr->param.version == -1) ? QR_VER_MAX : qr->param.version; + n = size - 4 - qr_vertable[v].nlen[mode]; + if (n <= 0) { + return 0; + } + + /* + * 符号化モードごとのデータサイズ + */ + switch (mode) { + case QR_EM_NUMERIC: + /* + * 数字モード: 3桁ごとに10ビット + * (余りは1桁なら4ビット, 2桁なら7ビット) + */ + l = (n / 10) * 3; + m = n % 10; + if (m >= 7) { + l += 2; + } else if (m >= 4) { + l += 1; + } + break; + case QR_EM_ALNUM: + /* + * 英数字モード: 2桁ごとに11ビット + * (余りは1桁につき6ビット) + */ + l = (n / 11) * 2; + m = n % 11; + if (m >= 6) { + l += 1; + } + break; + case QR_EM_8BIT: + /* + * 8ビットバイトモード: 1桁ごとに8ビット + */ + l = n / 8; + break; + case QR_EM_KANJI: + /* + * 漢字モード: 1文字(2バイト)ごとに13ビット + */ + l = (n / 13) * 2; + break; + default: + qrSetErrorInfo(qr, QR_ERR_INVALID_MODE, NULL); + return -1; + } + + return l; +} + +/* + * データを追加する + */ +QR_API int +qrAddData(QRCode *qr, const qr_byte_t *source, int size) +{ + if (qr->state == QR_STATE_FINAL) { + qrSetErrorInfo(qr, QR_ERR_STATE, _QR_FUNCTION); + return FALSE; + } + return qrAddData2(qr, source, size, qr->param.mode); +} + +/* + * 符号化モードを指定してデータを追加する + */ +QR_API int +qrAddData2(QRCode *qr, const qr_byte_t *source, int size, int mode) +{ + int enclen, maxlen; + int version; + int pos, err; + + if (qr->state == QR_STATE_FINAL) { + qrSetErrorInfo(qr, QR_ERR_STATE, _QR_FUNCTION); + return FALSE; + } + + if (size <= 0) { + qrSetErrorInfo(qr, QR_ERR_EMPTY_SRC, NULL); + return FALSE; + } + + /* + * 入力データに最適な符号化モードを選ぶ + */ + if (mode == QR_EM_AUTO) { + mode = qrDetectDataType(source, size); + } else if (mode < QR_EM_NUMERIC || mode >= QR_EM_COUNT) { + qrSetErrorInfo(qr, QR_ERR_INVALID_MODE, NULL); + return FALSE; + } + + + /* + * 符号化後のデータ長を計算する + */ + enclen = qrGetEncodedLength2(qr, size, mode); + if (enclen == -1) { + return FALSE; + } + version = (qr->param.version == -1) ? QR_VER_MAX : qr->param.version; + maxlen = 8 * qr_vertable[version].ecl[qr->param.eclevel].datawords; + if (qr->enclen + enclen > maxlen) { + qrSetErrorInfo3(qr, QR_ERR_LARGE_SRC, ", %d total encoded bits" + " (max %d bits on version=%d, ecl=%s)", + qr->enclen + enclen, maxlen, version, qr_eclname[qr->param.eclevel]); + return FALSE; + } + if (qr->param.version == -1) { + qr->delta1 += qr_vertable[QR_VER_MAX].nlen[mode] - qr_vertable[VERPOINT1].nlen[mode]; + qr->delta2 += qr_vertable[QR_VER_MAX].nlen[mode] - qr_vertable[VERPOINT2].nlen[mode]; + } + + /* + * 型番が指定されていれば、入力データをバッファリングせず直接エンコードする + */ + if (qr->param.version != -1) { + qr->enclen += enclen; + if (!qrHasData(qr)) { + qrInitDataWord(qr); + } + if (qrEncodeDataWord(qr, source, size, mode) == TRUE) { + qr->state = QR_STATE_SET; + return TRUE; + } + return FALSE; + } + + /* + * 入力データを検証する + */ + pos = -1; + err = QR_ERR_NONE; + switch (mode) { + case QR_EM_NUMERIC: + pos = qrStrPosNotNumeric(source, size); + err = QR_ERR_NOT_NUMERIC; + break; + case QR_EM_ALNUM: + pos = qrStrPosNotAlnum(source, size); + err = QR_ERR_NOT_ALNUM; + break; + case QR_EM_KANJI: + pos = qrStrPosNotKanji(source, size); + err = QR_ERR_NOT_KANJI; + break; + } + if (pos != -1) { + qrSetErrorInfo3(qr, err, " at offset %d", pos); + return FALSE; + } + qr->enclen += enclen; + + /* + * バッファの容量が足りないときは追加で確保する + */ + while (qr->srcmax < qr->srclen + size + 6) { + qr->srcmax += QR_SRC_MAX; + qr->source = (qr_byte_t *)realloc(qr->source, qr->srcmax); + if (qr->source == NULL) { + qr->srcmax = 0; + qrSetErrorInfo2(qr, QR_ERR_MEMORY_EXHAUSTED, _QR_FUNCTION); + return FALSE; + } + } + + /* + * バッファにデータを保存する + */ + qr->source[qr->srclen++] = (qr_byte_t)(mode | 0x80); + qr->source[qr->srclen++] = (qr_byte_t)((size >> 24) & 0x7F); + qr->source[qr->srclen++] = (qr_byte_t)((size >> 16) & 0xFF); + qr->source[qr->srclen++] = (qr_byte_t)((size >> 8) & 0xFF); + qr->source[qr->srclen++] = (qr_byte_t)(size & 0xFF); + memcpy(&(qr->source[qr->srclen]), source, (size_t)size); + qr->srclen += size; + qr->source[qr->srclen] = '\0'; + + qr->state = QR_STATE_SET; + return TRUE; +} + +/* + * 構造的連接の最後のQRコードオブジェクトにデータを追加する + */ +QR_API int +qrsAddData(QRStructured *st, const qr_byte_t *source, int size) +{ + if (st->state == QR_STATE_FINAL) { + qrSetErrorInfo(st->cur, QR_ERR_STATE, _QR_FUNCTION); + return FALSE; + } + return qrsAddData2(st, source, size, st->param.mode); +} + +/* + * 構造的連接の最後のQRコードオブジェクトに符号化モードを指定してデータを追加する + */ +QR_API int +qrsAddData2(QRStructured *st, const qr_byte_t *source, int size, int mode) +{ + int enclen, maxlen, limit, remain; + int sizes[QR_STA_MAX] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int i, j; + int p; + + if (st->state == QR_STATE_FINAL) { + qrSetErrorInfo(st->cur, QR_ERR_STATE, _QR_FUNCTION); + return FALSE; + } + + if (size <= 0) { + qrSetErrorInfo(st->cur, QR_ERR_EMPTY_SRC, NULL); + return FALSE; + } + + /* + * 入力データに最適な符号化モードを選ぶ + */ + if (mode == QR_EM_AUTO) { + mode = qrDetectDataType(source, size); + } + + /* + * 残りデータ容量を計算する + */ + maxlen = 8 * qr_vertable[st->param.version].ecl[st->param.eclevel].datawords; + limit = maxlen - QR_STA_LEN; + if (!qrHasData(st->cur)) { + remain = limit; + } else { + remain = qrRemainedDataBits(st->cur); + } + + /* + * 符号化後のデータ長を計算する + */ + enclen = qrGetEncodedLength2(st->cur, size, mode); + if (enclen == -1) { + return FALSE; + } + j = 0; + if (enclen > remain) { + int l, r, s; + r = remain; + s = size; + for (i = 0; i <= st->max - st->num; i++) { + j++; + l = qrGetEncodableLength2(st->cur, r, mode); + if (s <= l) { + sizes[i] = s; + s = 0; + break; + } else { + sizes[i] = l; + s -= l; + r = limit; + } + } + if (s > 0) { + int snum, reqlen; + snum = (enclen + maxlen - 1) / maxlen - (st->num - 1); + reqlen = maxlen * (st->num - 1); + reqlen += maxlen - remain; + reqlen += enclen; + reqlen += qr_vertable[st->param.version].nlen[mode] * (snum - 1) + QR_STA_LEN * snum; + qrSetErrorInfo3(st->cur, QR_ERR_LARGE_SRC, ", %d total encoded bits" + " (max %d bits on version=%d, ecl=%s, num=%d)", + reqlen, maxlen * st->max, + st->param.version, qr_eclname[st->param.eclevel], st->max); + return FALSE; + } + } else { + j = 1; + sizes[0] = size; + } + + /* + * 入力データをエンコードする + */ + p = 0; + i = 0; + while (i < j) { + if (sizes[i] == 0 && i != 0) { + break; + } + if (!qrHasData(st->cur)) { + /* + * データコード語を初期化し、仮の構造的連接ヘッダを追加する + */ + qrInitDataWord(st->cur); + qrAddDataBits(st->cur, 4, 0); + qrAddDataBits(st->cur, 4, 0); + qrAddDataBits(st->cur, 4, 0); + qrAddDataBits(st->cur, 8, 0); + } + if (sizes[i] != 0) { + st->cur->enclen += qrGetEncodedLength2(st->cur, sizes[i], mode); + if (qrEncodeDataWord(st->cur, source + p, sizes[i], mode) == TRUE) { + st->cur->state = QR_STATE_SET; + st->state = QR_STATE_SET; + } else { + return FALSE; + } + p += sizes[i]; + } + i++; + if (i < j && sizes[i] > 0) { + /* + * 次のQRコードオブジェクトを初期化する + */ + int errcode; + st->qrs[st->num] = qrInit(st->param.version, st->param.mode, + st->param.eclevel, st->param.masktype, &errcode); + if (st->qrs[st->num] == NULL) { + qrSetErrorInfo(st->cur, errcode, NULL); + return FALSE; + } + st->cur = st->qrs[st->num]; + st->num++; + } + } + + /* + * パリティを計算する + */ + p = 0; + while (p < size) { + st->parity ^= source[p++]; + } + + return TRUE; +} + +/* + * データコード語を初期化する + */ +static int +qrInitDataWord(QRCode *qr) +{ + /* + * データコード語領域をゼロクリアする + */ + memset(qr->dataword, '\0', QR_DWD_MAX); + + /* + * 追加位置をバイト0の最上位ビットにする + */ + qr->dwpos = 0; + qr->dwbit = 7; + + return TRUE; +} + +/* + * データコード語をエンコードする + */ +static int +qrEncodeDataWord(QRCode *qr, const qr_byte_t *source, int size, int mode) +{ + int p = 0; + int e = QR_ERR_NONE; + int n = 0; + int word = 0; + int dwpos = qr->dwbit; + int dwbit = qr->dwpos; + + if (mode < QR_EM_NUMERIC || mode >= QR_EM_COUNT) { + e = QR_ERR_INVALID_MODE; + goto err; + } + + /* + * モード指示子(4ビット)を追加する + */ + qrAddDataBits(qr, 4, qr_modeid[mode]); + + /* + * 文字数指示子(8〜16ビット)を追加する + * ビット数は型番とモードによって異なる + */ + if (mode == QR_EM_KANJI) { + qrAddDataBits(qr, qr_vertable[qr->param.version].nlen[mode], size / 2); + } else { + qrAddDataBits(qr, qr_vertable[qr->param.version].nlen[mode], size); + } + + /* + * 入力データを符号化する + */ + switch (mode) { + case QR_EM_NUMERIC: + /* + * 数字モード + * 3桁ずつ10ビットの2進数に変換する + * 余りは1桁なら4ビット、2桁なら7ビットにする + */ + while (p < size) { + qr_byte_t q = source[p]; + if (q < '0' || q > '9') { + /* 数字でない */ + e = QR_ERR_NOT_NUMERIC; + goto err; + } + word = word * 10 + (q - '0'); + /* + * 3桁たまったら10ビットで追加する + */ + if (++n >= 3) { + qrAddDataBits(qr, 10, word); + n = 0; + word = 0; + } + p++; + } + /* + * 余りの桁を追加する + */ + if (n == 1) { + qrAddDataBits(qr, 4, word); + } else if (n == 2) { + qrAddDataBits(qr, 7, word); + } + break; + + case QR_EM_ALNUM: + /* + * 英数字モード + * 2桁ずつ11ビットの2進数に変換する + * 余りは6ビットとして変換する + */ + while (p < size) { + signed char q = qr_alnumtable[source[p]]; + if (q == -1) { + /* 符号化可能な英数字でない */ + e = QR_ERR_NOT_ALNUM; + goto err; + } + word = word * 45 + (int)q; + /* + * 2桁たまったら11ビットで追加する + */ + if (++n >= 2) { + qrAddDataBits(qr, 11, word); + n = 0; + word = 0; + } + p++; + } + /* + * 余りの桁を追加する + */ + if (n == 1) { + qrAddDataBits(qr, 6, word); + } + break; + + case QR_EM_8BIT: + /* + * 8ビットバイトモード + * 各バイトを直接8ビット値として追加する + */ + while (p < size) { + qrAddDataBits(qr, 8, (int)source[p++]); + } + break; + + case QR_EM_KANJI: + /* + * 漢字モード + * 2バイトを13ビットに変換して追加する + */ + while (p < size - 1) { + qr_byte_t x, y; + /* + * 第1バイトの処理 + * 0x81-0x9fなら0x81を引く + * 0xe0,0xeaなら0xc1を引く + */ + x = source[p++]; + if (x >= 0x81 && x <= 0x9f) { + x -= 0x81; + } else if (x >= 0xe0 && x <= 0xea) { + x -= 0xc1; + } else { + /* JIS X 0208漢字の1バイトめでない */ + p -= 1; + e = QR_ERR_NOT_KANJI; + goto err; + } + /* + * 第2バイトの処理 + * 0x40を引く + */ + y = source[p++]; + if (y >= 0x40 && y <= 0xfc) { + y -= 0x40; + } else { + /* JIS X 0208漢字の2バイトめでない */ + p -= 1; + e = QR_ERR_NOT_KANJI; + goto err; + } + /* + * 結果を13ビットの値として追加する + */ + word = (int)qr_dwtable_kanji[x][y]; + if (word == -1) { + /* JIS X 0208漢字の未定義領域 */ + p -= 2; + e = QR_ERR_NOT_KANJI; + goto err; + } + qrAddDataBits(qr, 13, word); + } + if (p < size) { + /* + * 末尾に余分なバイトがある + */ + e = QR_ERR_NOT_KANJI; + goto err; + } + break; + + default: + e = QR_ERR_INVALID_MODE; + goto err; + } + + return TRUE; + + err: + qr->dwpos = dwpos; + qr->dwbit = dwbit; + if (e == QR_ERR_INVALID_MODE) { + qrSetErrorInfo(qr, e, NULL); + } else { + qrSetErrorInfo3(qr, e, " at offset %d", p); + } + return FALSE; +} + +/* + * データコード語の余りを埋める + */ +static int +qrFinalizeDataWord(QRCode *qr) +{ + int n, m; + int word; + + /* + * 終端パターンを追加する(最大4ビットの0) + */ + n = qrRemainedDataBits(qr); + if (n < 4) { + qrAddDataBits(qr, n, 0); + n = 0; + } else { + qrAddDataBits(qr, 4, 0); + n -= 4; + } + /* + * 末尾のデータコード語の全ビットが埋まっていなければ + * 余りを埋め草ビット(0)で埋める + */ + m = n % 8; + if (m > 0) { + qrAddDataBits(qr, m, 0); + n -= m; + } + + /* + * 残りのデータコード語に埋め草コード語1,2を交互に埋める + */ + word = PADWORD1; + while (n >= 8) { + qrAddDataBits(qr, 8, word); + if (word == PADWORD1) { + word = PADWORD2; + } else { + word = PADWORD1; + } + n -= 8; + } + + return TRUE; +} + +/* + * データコード語にビット列を追加する + */ +static void +qrAddDataBits(QRCode *qr, int n, int word) +{ + /* + * 上位ビットから順に処理(ビット単位で処理するので遅い) + */ + while (n-- > 0) { + /* + * ビット追加位置にデータの下位からnビットめをORする + */ + qr->dataword[qr->dwpos] |= ((word >> n) & 1) << qr->dwbit; + /* + * 次のビット追加位置に進む + */ + if (--qr->dwbit < 0) { + qr->dwpos++; + qr->dwbit = 7; + } + } +} + +/* + * データコード語の残りビット数を返す + */ +QR_API int +qrRemainedDataBits(QRCode *qr) +{ + int version; + version = (qr->param.version == -1) ? QR_VER_MAX : qr->param.version; + return (qr_vertable[version].ecl[qr->param.eclevel].datawords - qr->dwpos) * 8 - (7 - qr->dwbit); +} + +/* + * RSブロックごとに誤り訂正コード語を計算する + */ +static int +qrComputeECWord(QRCode *qr) +{ + int i, j, k, m; + int ecwtop, dwtop, nrsb, rsbnum; + qr_byte_t rswork[QR_RSD_MAX]; + + /* + * データコード語をRSブロックごとに読み出し、 + * それぞれについて誤り訂正コード語を計算する + * RSブロックは長さによってnrsb種類に分かれ、 + * それぞれの長さについてrsbnum個のブロックがある + */ + dwtop = 0; + ecwtop = 0; + nrsb = qr_vertable[qr->param.version].ecl[qr->param.eclevel].nrsb; +#define rsb qr_vertable[qr->param.version].ecl[qr->param.eclevel].rsb +#define gfvector qr_gftable[ecwlen] + for (i = 0; i < nrsb; i++) { + int dwlen, ecwlen; + /*unsigned char *gfvector;*/ + /*qr_rsblock_t *rsbp;*/ + /* + * この長さのRSブロックの個数(rsbnum)と + * RSブロック内のデータコード語の長さ(dwlen)、 + * 誤り訂正コード語の長さ(ecwlen)を求める + * また誤り訂正コード語の長さから、使われる + * 誤り訂正生成多項式(gfvector)を選ぶ + */ + /*rsbp = &(qr_vertable[qr->param.version].ecl[qr->param.eclevel].rsb[i]); + rsbnum = rsbp->rsbnum; + dwlen = rsbp->datawords; + ecwlen = rsbp->totalwords - rsbp->datawords;*/ + rsbnum = rsb[i].rsbnum; + dwlen = rsb[i].datawords; + ecwlen = rsb[i].totalwords - rsb[i].datawords; + /*gfvector = qr_gftable[ecwlen];*/ + /* + * それぞれのRSブロックについてデータコード語を + * 誤り訂正生成多項式で除算し、結果を誤り訂正 + * コード語とする + */ + for (j = 0; j < rsbnum; j++) { + /* + * RS符号計算用作業領域をクリアし、 + * 当該RSブロックのデータコード語を + * 多項式係数とみなして作業領域に入れる + * (作業領域の大きさはRSブロックの + * データコード語と誤り訂正コード語の + * いずれか長いほうと同じだけ必要) + */ + memset(&(rswork[0]), '\0', QR_RSD_MAX); + memcpy(&(rswork[0]), &(qr->dataword[dwtop]), (size_t)dwlen); + /* + * 多項式の除算を行う + * (各次数についてデータコード語の初項係数から + * 誤り訂正生成多項式への乗数を求め、多項式 + * どうしの減算により剰余を求めることをくり返す) + */ + for (k = 0; k < dwlen; k++) { + int e; + if (rswork[0] == 0) { + /* + * 初項係数がゼロなので、各項係数を + * 左にシフトして次の次数に進む + */ + for (m = 0; m < QR_RSD_MAX-1; m++) { + rswork[m] = rswork[m+1]; + } + rswork[QR_RSD_MAX-1] = 0; + continue; + } + /* + * データコード語の初項係数(整数表現)から + * 誤り訂正生成多項式への乗数(べき表現)を求め、 + * 残りの各項について剰余を求めるために + * データコード語の各項係数を左にシフトする + */ + e = qr_fac2exp[rswork[0]]; + for (m = 0; m < QR_RSD_MAX-1; m++) { + rswork[m] = rswork[m+1]; + } + rswork[QR_RSD_MAX-1] = 0; + /* + * 誤り訂正生成多項式の各項係数に上で求めた + * 乗数を掛け(べき表現の加算により求める)、 + * データコード語の各項から引いて(整数表現の + * 排他的論理和により求める)、剰余を求める + */ + for (m = 0; m < ecwlen; m++) { + rswork[m] ^= qr_exp2fac[(gfvector[m] + e) % 255]; + } + } + /* + * 多項式除算の剰余を当該RSブロックの + * 誤り訂正コードとする + */ + memcpy(&(qr->ecword[ecwtop]), &(rswork[0]), (size_t)ecwlen); + /* + * データコード語の読み出し位置と + * 誤り訂正コード語の書き込み位置を + * 次のRSブロック開始位置に移動する + */ + dwtop += dwlen; + ecwtop += ecwlen; + } + } +#undef rsb +#undef gfvector + return TRUE; +} + +/* + * データコード語と誤り訂正コード語から最終的なコード語を作る + */ +static int +qrMakeCodeWord(QRCode *qr) +{ + int i, j, k, cwtop, pos; + int dwlenmax, ecwlenmax; + int dwlen, ecwlen, nrsb; + /*qr_rsblock_t *rsb;*/ + + /* + * RSブロックのサイズ種類数(nrsb)および + * 最大RSブロックのデータコード語数(dwlenmax)、 + * 誤り訂正コード語数(ecwlenmax)を得る + */ + nrsb = qr_vertable[qr->param.version].ecl[qr->param.eclevel].nrsb; + /*rsb = qr_vertable[qr->param.version].ecl[qr->param.eclevel].rsb;*/ +#define rsb qr_vertable[qr->param.version].ecl[qr->param.eclevel].rsb + dwlenmax = rsb[nrsb-1].datawords; + ecwlenmax = rsb[nrsb-1].totalwords - rsb[nrsb-1].datawords; + /* + * 各RSブロックから順にデータコード語を取り出し + * コード語領域(qr->codeword)に追加する + */ + cwtop = 0; + for (i = 0; i < dwlenmax; i++) { + pos = i; + /* + * RSブロックのサイズごとに処理を行う + */ + for (j = 0; j < nrsb; j++) { + dwlen = rsb[j].datawords; + /* + * 同じサイズのRSブロックは順に処理する + */ + for (k = 0; k < rsb[j].rsbnum; k++) { + /* + * 各RSブロックのiバイトめのデータ + * コード語をコード語領域に追加する + * (すでにすべてのデータコード語を + * 取り出したRSブロックは飛ばす) + */ + if (i < dwlen) { + qr->codeword[cwtop++] = qr->dataword[pos]; + } + /* + * 次のRSブロックのiバイトめに進む + */ + pos += dwlen; + } + } + } + /* + * 各RSブロックから順に誤り訂正コード語を取り出し + * コード語領域(qr->codeword)に追加する + */ + for (i = 0; i < ecwlenmax; i++) { + pos = i; + /* + * RSブロックのサイズごとに処理を行う + */ + for (j = 0; j < nrsb; j++) { + ecwlen = rsb[j].totalwords - rsb[j].datawords; + /* + * 同じサイズのRSブロックは順に処理する + */ + for (k = 0; k < rsb[j].rsbnum; k++) { + /* + * 各RSブロックのiバイトめの誤り訂正 + * コード語をコード語領域に追加する + * (すでにすべての誤り訂正コード語を + * 取り出したRSブロックは飛ばす) + */ + if (i < ecwlen) { + qr->codeword[cwtop++] = qr->ecword[pos]; + } + /* + * 次のRSブロックのiバイトめに進む + */ + pos += ecwlen; + } + } + } +#undef rsb +#undef nrsb + return TRUE; +} + +/* + * シンボルを初期化し、機能パターンを配置する + */ +static int +qrFillFunctionPattern(QRCode *qr) +{ + int i, j, n, dim, xpos, ypos; + + /* + * シンボルの1辺の長さを求める + */ + dim = qr_vertable[qr->param.version].dimension; + /* + * シンボル全体をクリアする + */ + qrFree(qr->symbol); + qrFree(qr->_symbol); + qr->_symbol = (qr_byte_t *)calloc((size_t)dim, (size_t)dim); + if (qr->_symbol == NULL) { + return FALSE; + } + qr->symbol = (qr_byte_t **)malloc(sizeof(qr_byte_t *) * (size_t)dim); + if (qr->symbol == NULL) { + free(qr->_symbol); + return FALSE; + } + for (i = 0; i < dim; i++) { + qr->symbol[i] = qr->_symbol + dim * i; + } + /* + * 左上、右上、左下の隅に位置検出パターンを配置する + */ + for (i = 0; i < QR_DIM_FINDER; i++) { + for (j = 0; j < QR_DIM_FINDER; j++) { + qr->symbol[i][j] = qr_finderpattern[i][j]; + qr->symbol[i][dim-1-j] = qr_finderpattern[i][j]; + qr->symbol[dim-1-i][j] = qr_finderpattern[i][j]; + } + } + /* + * 位置検出パターンの分離パターンを配置する + */ + for (i = 0; i < QR_DIM_FINDER+1; i++) { + qr->symbol[i][QR_DIM_FINDER] = QR_MM_FUNC; + qr->symbol[QR_DIM_FINDER][i] = QR_MM_FUNC; + qr->symbol[i][dim-1-QR_DIM_FINDER] = QR_MM_FUNC; + qr->symbol[dim-1-QR_DIM_FINDER][i] = QR_MM_FUNC; + qr->symbol[dim-1-i][QR_DIM_FINDER] = QR_MM_FUNC; + qr->symbol[QR_DIM_FINDER][dim-1-i] = QR_MM_FUNC; + } + /* + * 位置合わせパターンを配置する + */ + n = qr_vertable[qr->param.version].aplnum; + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + int x, y, x0, y0, xcenter, ycenter; + /* + * 位置合わせパターンの中心と左上の座標を求める + */ + ycenter = qr_vertable[qr->param.version].aploc[i]; + xcenter = qr_vertable[qr->param.version].aploc[j]; + y0 = ycenter - QR_DIM_ALIGN / 2; + x0 = xcenter - QR_DIM_ALIGN / 2; + if (qrIsFunc(qr, ycenter, xcenter)) { + /* + * 位置検出パターンと重なるときは配置しない + */ + continue; + } + for (y = 0; y < QR_DIM_ALIGN; y++) { + for (x = 0; x < QR_DIM_ALIGN; x++) { + qr->symbol[y0+y][x0+x] = qr_alignpattern[y][x]; + } + } + } + } + /* + * タイミングパターンを配置する + */ + for (i = QR_DIM_FINDER; i < dim-1-QR_DIM_FINDER; i++) { + qr->symbol[i][QR_DIM_TIMING] = QR_MM_FUNC; + qr->symbol[QR_DIM_TIMING][i] = QR_MM_FUNC; + if ((i & 1) == 0) { + qr->symbol[i][QR_DIM_TIMING] |= QR_MM_BLACK; + qr->symbol[QR_DIM_TIMING][i] |= QR_MM_BLACK; + } + } + /* + * 形式情報の領域を予約する + */ + for (i = 0; i < 2; i++) { + for (j = 0; j < QR_FIN_MAX; j++) { + xpos = (qr_fmtinfopos[i][j].xpos + dim) % dim; + ypos = (qr_fmtinfopos[i][j].ypos + dim) % dim; + qr->symbol[ypos][xpos] |= QR_MM_FUNC; + } + } + xpos = (qr_fmtblackpos.xpos + dim) % dim; + ypos = (qr_fmtblackpos.ypos + dim) % dim; + qr->symbol[ypos][xpos] |= QR_MM_FUNC; + /* + * 型番情報が有効(型番7以上)なら + * 型番情報の領域を予約する + */ + if (qr_verinfo[qr->param.version] != -1L) { + for (i = 0; i < 2; i++) { + for (j = 0; j < QR_VIN_MAX; j++) { + xpos = (qr_verinfopos[i][j].xpos + dim) % dim; + ypos = (qr_verinfopos[i][j].ypos + dim) % dim; + qr->symbol[ypos][xpos] |= QR_MM_FUNC; + } + } + } + + return TRUE; +} + +/* + * シンボルに符号化されたコード語を配置する + */ +static int +qrFillCodeWord(QRCode *qr) +{ + int i, j; + + /* + * シンボル右下隅から開始する + */ + qrInitPosition(qr); + /* + * コード語領域のすべてのバイトについて... + */ + for (i = 0; i < qr_vertable[qr->param.version].totalwords; i++) { + /* + * 最上位ビットから順に各ビットを調べ... + */ + for (j = 7; j >= 0; j--) { + /* + * そのビットが1なら黒モジュールを置く + */ + if ((qr->codeword[i] & (1 << j)) != 0) { + qr->symbol[qr->ypos][qr->xpos] |= QR_MM_DATA; + } + /* + * 次のモジュール配置位置に移動する + */ + qrNextPosition(qr); + } + } + + return TRUE; +} + +/* + * モジュール配置の初期位置と配置方向を決める + */ +static void +qrInitPosition(QRCode *qr) +{ + /* + * シンボルの右下隅から配置を始める + */ + qr->xpos = qr->ypos = qr_vertable[qr->param.version].dimension - 1; + /* + * 最初の移動方向は左向き、次に上向き + */ + qr->xdir = -1; + qr->ydir = -1; +} + +/* + * 次のモジュール配置位置を決める + */ +static void +qrNextPosition(QRCode *qr) +{ + do { + /* + * qr->xdir方向に1モジュール移動して + * qr->xdirの向きを逆にする + * 右に移動したときはqr->ydir方向にも + * 1モジュール移動する + */ + qr->xpos += qr->xdir; + if (qr->xdir > 0) { + qr->ypos += qr->ydir; + } + qr->xdir = -qr->xdir; + /* + * y方向にシンボルをはみ出すようなら + * y方向ではなくx方向に2モジュール左に移動し、 + * かつqr->ydirの向きを逆にする + * qr->xposが縦のタイミングパターン上なら + * さらに1モジュール左に移動する + */ + if (qr->ypos < 0 || qr->ypos >= qr_vertable[qr->param.version].dimension) { + qr->ypos -= qr->ydir; + qr->ydir = -qr->ydir; + qr->xpos -= 2; + if (qr->xpos == QR_DIM_TIMING) { + qr->xpos--; + } + } + /* + * 新しい位置が機能パターン上なら + * それをよけて次の候補位置を探す + */ + } while (qrIsFunc(qr, qr->ypos, qr->xpos)); +} + +/* + * シンボルを最適なマスクパターンでマスクする + */ +static int +qrSelectMaskPattern(QRCode *qr) +{ + int type; + long penalty, xpenalty; + + if (qr->param.masktype >= 0) { + /* + * マスクパターンが引数で指定されていたので + * そのパターンでマスクして終了 + */ + return qrApplyMaskPattern(qr); + } + /* + * すべてのマスクパターンを評価する + */ + xpenalty = -1L; + for (type = 0; type < QR_MPT_MAX; type++) { + /* + * 当該マスクパターンでマスクして評価する + */ + qrApplyMaskPattern2(qr, type); + penalty = qrEvaluateMaskPattern(qr); + /* + * 失点がこれまでより低かったら記録する + */ + if (xpenalty == -1L || penalty < xpenalty) { + qr->param.masktype = type; + xpenalty = penalty; + } + } + /* + * 失点が最低のパターンでマスクする + */ + return qrApplyMaskPattern(qr); +} + +/* + * 設定済みの参照子のマスクパターンでシンボルをマスクする + */ +static int +qrApplyMaskPattern(QRCode *qr) +{ + return qrApplyMaskPattern2(qr, qr->param.masktype); +} + +/* + * 指定した参照子のマスクパターンでシンボルをマスクする + */ +static int +qrApplyMaskPattern2(QRCode *qr, int type) +{ + + int i, j, dim; + + if (type < 0 || type >= QR_MPT_MAX) { + qrSetErrorInfo3(qr, QR_ERR_INVALID_MPT, "%d", type); + return FALSE; + } + + dim = qr_vertable[qr->param.version].dimension; + /* + * 以前のマスクパターンをクリアし、 + * 符号化済みデータを初期パターンとする + */ + for (i = 0; i < dim; i++) { + for (j = 0; j < dim; j++) { + /* + * 機能パターン領域の印字黒モジュールは残す + */ + if (qrIsFunc(qr, i, j)) { + continue; + } + /* + * 符号化データ領域は符号化データの + * 黒モジュールを印字黒モジュールにする + */ + if (qrIsData(qr, i, j)) { + qr->symbol[i][j] |= QR_MM_BLACK; + } else { + qr->symbol[i][j] &= ~QR_MM_BLACK; + } + } + } + /* + * i行j列のモジュールについて... + */ + for (i = 0; i < dim; i++) { + for (j = 0; j < dim; j++) { + if (qrIsFunc(qr, i, j)) { + /* + * 機能パターン領域(および形式情報、 + * 型番情報)はマスク対象から除外する + */ + continue; + } + /* + * 指定された条件を満たすモジュールを反転する + */ + if ((type == 0 && (i + j) % 2 == 0) || + (type == 1 && i % 2 == 0) || + (type == 2 && j % 3 == 0) || + (type == 3 && (i + j) % 3 == 0) || + (type == 4 && ((i / 2) + (j / 3)) % 2 == 0) || + (type == 5 && (i * j) % 2 + (i * j) % 3 == 0) || + (type == 6 && ((i * j) % 2 + (i * j) % 3) % 2 == 0) || + (type == 7 && ((i * j) % 3 + (i + j) % 2) % 2 == 0)) + { + qr->symbol[i][j] ^= QR_MM_BLACK; + } + } + } + + return TRUE; +} + +/* + * マスクパターンを評価し評価値を返す + */ +static long +qrEvaluateMaskPattern(QRCode *qr) +{ + int i, j, m, n, dim; + long penalty; + + /* + * 評価値をpenaltyに積算する + * マスクは符号化領域に対してのみ行うが + * 評価はシンボル全体について行われる + */ + penalty = 0L; + dim = qr_vertable[qr->param.version].dimension; + /* + * 特徴: 同色の行/列の隣接モジュール + * 評価条件: モジュール数 = (5+i) + * 失点: 3+i + */ + for (i = 0; i < dim; i++) { + n = 0; + for (j = 0; j < dim; j++) { + if (j > 0 && qrIsBlack(qr, i, j) == qrIsBlack(qr, i, j-1)) { + /* + * すぐ左と同色のモジュール + * 同色列の長さを1増やす + */ + n++; + } else { + /* + * 色が変わった + * 直前で終わった同色列の長さを評価する + */ + if (n >= 5) { + penalty += (long)(3 + (n - 5)); + } + n = 1; + } + } + /* + * 列が尽きた + * 直前で終わった同色列の長さを評価する + */ + if (n >= 5) { + penalty += (long)(3 + (n - 5)); + } + } + for (i = 0; i < dim; i++) { + n = 0; + for (j = 0; j < dim; j++) { + if (j > 0 && qrIsBlack(qr, j, i) == qrIsBlack(qr, j-1, i)) { + /* + * すぐ上と同色のモジュール + * 同色列の長さを1増やす + */ + n++; + } else { + /* + * 色が変わった + * 直前で終わった同色列の長さを評価する + */ + if (n >= 5) { + penalty += (long)(3 + (n - 5)); + } + n = 1; + } + } + /* + * 列が尽きた + * 直前で終わった同色列の長さを評価する + */ + if (n >= 5) { + penalty += (long)(3 + (n - 5)); + } + } + /* + * 特徴: 同色のモジュールブロック + * 評価条件: ブロックサイズ = 2×2 + * 失点: 3 + */ + for (i = 0; i < dim - 1; i++) { + for (j = 0; j < dim - 1; j++) { + if (qrIsBlack(qr, i, j) == qrIsBlack(qr, i, j+1) && + qrIsBlack(qr, i, j) == qrIsBlack(qr, i+1, j) && + qrIsBlack(qr, i, j) == qrIsBlack(qr, i+1, j+1)) + { + /* + * 2×2の同色のブロックがあった + */ + penalty += 3L; + } + } + } + /* + * 特徴: 行/列における1:1:3:1:1比率(暗:明:暗:明:暗)のパターン + * に続いて比率4の幅以上の明パターン + * 失点: 40 + * 前後はシンボル境界外か明モジュールである必要がある + * 2:2:6:2:2のようなパターンにも失点を与えるべきかは + * JIS規格からは読み取れない。ここでは与えていない + */ + for (i = 0; i < dim; i++) { + for (j = 0; j < dim - 6; j++) { + if ((j == 0 || !qrIsBlack(qr, i, j-1)) && + qrIsBlack(qr, i, j+0) && + !qrIsBlack(qr, i, j+1) && + qrIsBlack(qr, i, j+2) && + qrIsBlack(qr, i, j+3) && + qrIsBlack(qr, i, j+4) && + !qrIsBlack(qr, i, j+5) && + qrIsBlack(qr, i, j+6)) + { + int k, l; + l = 1; + for (k = 0; k < dim - j - 7 && k < 4; k++) { + if (qrIsBlack(qr, i, j + k + 7)) { + l = 0; + break; + } + } + /* + * パターンがあった + */ + if (l) { + penalty += 40L; + } + } + } + } + for (i = 0; i < dim; i++) { + for (j = 0; j < dim - 6; j++) { + if ((j == 0 || !qrIsBlack(qr, j-1, i)) && + qrIsBlack(qr, j+0, i) && + !qrIsBlack(qr, j+1, i) && + qrIsBlack(qr, j+2, i) && + qrIsBlack(qr, j+3, i) && + qrIsBlack(qr, j+4, i) && + !qrIsBlack(qr, j+5, i) && + qrIsBlack(qr, j+6, i) && + (j == dim-7 || !qrIsBlack(qr, j+7, i))) + { + int k, l; + l = 1; + for (k = 0; k < dim - j - 7 && k < 4; k++) { + if (qrIsBlack(qr, j + k + 7, i)) { + l = 0; + break; + } + } + /* + * パターンがあった + */ + if (l) { + penalty += 40L; + } + } + } + } + /* + * 特徴: 全体に対する暗モジュールの占める割合 + * 評価条件: 50±(5×k)%〜50±(5×(k+1))% + * 失点: 10×k + */ + m = 0; + n = 0; + for (i = 0; i < dim; i++) { + for (j = 0; j < dim; j++) { + m++; + if (qrIsBlack(qr, i, j)) { + n++; + } + } + } + penalty += (long)(abs((n * 100 / m) - 50) / 5 * 10); + return penalty; +} + +/* + * シンボルに形式情報と型番情報を配置する + */ +static int +qrFillFormatInfo(QRCode *qr) +{ + int i, j, dim, fmt, modulo, xpos, ypos; + long v; + + dim = qr_vertable[qr->param.version].dimension; + /* + * 形式情報を計算する + * 誤り訂正レベル2ビット(L:01, M:00, Q:11, H:10)と + * マスクパターン参照子3ビットからなる計5ビットに + * Bose-Chaudhuri-Hocquenghem(15,5)符号による + * 誤り訂正ビット10ビットを付加して15ビットとする + * (5ビットをxの次数14〜10の多項式係数とみなして + * 多項式x^10+x^8+x^5+x^4+x^2+x+1(係数10100110111) + * で除算した剰余10ビットを誤り訂正ビットとする) + * さらにすべてのビットがゼロにならないように + * 101010000010010(0x5412)とXORをとる + */ + fmt = ((qr->param.eclevel ^ 1) << 3) | qr->param.masktype; + modulo = fmt << 10; + for (i = 14; i >= 10; i--) { + if ((modulo & (1 << i)) == 0) { + continue; + } + modulo ^= 0x537 << (i - 10); + } + fmt = ((fmt << 10) + modulo) ^ 0x5412; + /* + * 形式情報をシンボルに配置する + */ + for (i = 0; i < 2; i++) { + for (j = 0; j < QR_FIN_MAX; j++) { + if ((fmt & (1 << j)) == 0) { + continue; + } + xpos = (qr_fmtinfopos[i][j].xpos + dim) % dim; + ypos = (qr_fmtinfopos[i][j].ypos + dim) % dim; + qr->symbol[ypos][xpos] |= QR_MM_BLACK; + } + } + xpos = (qr_fmtblackpos.xpos + dim) % dim; + ypos = (qr_fmtblackpos.ypos + dim) % dim; + qr->symbol[ypos][xpos] |= QR_MM_BLACK; + /* + * 型番情報が有効(型番7以上)なら + * 型番情報をシンボルに配置する + */ + v = qr_verinfo[qr->param.version]; + if (v != -1L) { + for (i = 0; i < 2; i++) { + for (j = 0; j < QR_VIN_MAX; j++) { + if ((v & (1L << j)) == 0L) { + continue; + } + xpos = (qr_verinfopos[i][j].xpos + dim) % dim; + ypos = (qr_verinfopos[i][j].ypos + dim) % dim; + qr->symbol[ypos][xpos] |= QR_MM_BLACK; + } + } + } + + return TRUE; +} + +/* + * データコード語の余剰ビットを埋める処理から + * シンボルに形式情報と型番情報を配置する処理までを + * 一括で実行する + */ +QR_API int +qrFinalize(QRCode *qr) +{ + static qr_funcs funcs[] = { + qrFinalizeDataWord, + qrComputeECWord, + qrMakeCodeWord, + qrFillFunctionPattern, + qrFillCodeWord, + qrSelectMaskPattern, + qrFillFormatInfo, + NULL + }; + int i = 0; + int ret = TRUE; + + if (qrIsFinalized(qr)) { + return TRUE; + } + + /* + * 型番自動選択 + */ + if (qr->param.version == -1) { + int maxlen, delta; + int version = 0; + while (++version <= QR_VER_MAX) { + if (version <= VERPOINT1) { + delta = qr->delta1; + } else if (version <= VERPOINT2) { + delta = qr->delta2; + } else { + delta = 0; + } + maxlen = 8 * qr_vertable[version].ecl[qr->param.eclevel].datawords; + if (maxlen >= qr->enclen - delta) { + break; + } + } + if (version > QR_VER_MAX) { + maxlen = 8 * qr_vertable[QR_VER_MAX].ecl[qr->param.eclevel].datawords; + qrSetErrorInfo3(qr, QR_ERR_LARGE_SRC, ", %d total encoded bits" + " (max %d bits on version=%d, ecl=%s)", + qr->enclen, maxlen, QR_VER_MAX, qr_eclname[qr->param.eclevel]); + return FALSE; + } + qr->param.version = version; + } + + /* + * データコード語に入力データを登録する + */ + if (qr->source != NULL) { + qr_byte_t *source; + int mode, size; + + qrInitDataWord(qr); + source = qr->source; + while ((mode = (int)(*source++)) != '\0') { + mode ^= 0x80; + size = ((int)*source++) << 24; + size |= ((int)*source++) << 16; + size |= ((int)*source++) << 8; + size |= (int)*source++; + if (qrEncodeDataWord(qr, source, size, mode) == FALSE) { + return FALSE; + } + source += size; + } + + qrFree(qr->source); + } + + /* + * シンボルを生成する + */ + while (funcs[i] && ret == TRUE) { + ret = funcs[i++](qr); + } + + if (ret == TRUE) { + qrFree(qr->dataword); + qrFree(qr->ecword); + qrFree(qr->codeword); + qr->state = QR_STATE_FINAL; + } + return ret; +} + +/* + * Finalze済か判定する + */ +QR_API int +qrIsFinalized(const QRCode *qr) +{ + if (qr->state == QR_STATE_FINAL) { + return TRUE; + } + return FALSE; +} + +/* + * データをセット済か判定する + */ +QR_API int qrHasData(const QRCode *qr) +{ + if (qr->state == QR_STATE_BEGIN) { + return FALSE; + } + return TRUE; +} + +/* + * 構造的連接の最後のQRコードオブジェクトの + * 仮構造的連接ヘッダを正しい情報で上書きし、Finalizeする + */ +QR_API int +qrsFinalize(QRStructured *st) +{ + int m, n, r; + + if (!qrsHasData(st)) { + qrSetErrorInfo(st->cur, QR_ERR_STATE, _QR_FUNCTION); + return FALSE; + } else if (qrsIsFinalized(st)) { + return TRUE; + } + + m = 0; + n = st->num - 1; + r = TRUE; + while (m <= n && r == TRUE) { + int dwpos, dwbit; + dwpos = st->qrs[m]->dwpos; + dwbit = st->qrs[m]->dwbit; + st->qrs[m]->dwpos = 0; + st->qrs[m]->dwbit = 7; + qrAddDataBits(st->qrs[m], 4, 3); + qrAddDataBits(st->qrs[m], 4, m); + qrAddDataBits(st->qrs[m], 4, n); + qrAddDataBits(st->qrs[m], 8, st->parity); + st->qrs[m]->dwpos = dwpos; + st->qrs[m]->dwbit = dwbit; + r = qrFinalize(st->qrs[m]); + m++; + } + + if (r == TRUE) { + st->state = QR_STATE_FINAL; + } + return r; +} + +/* + * Finalze済か判定する + */ +QR_API int +qrsIsFinalized(const QRStructured *st) +{ + if (st->state == QR_STATE_FINAL) { + return TRUE; + } + return FALSE; +} + +/* + * データをセット済か判定する + */ +QR_API int qrsHasData(const QRStructured *st) +{ + if (st->state == QR_STATE_BEGIN) { + return FALSE; + } + return TRUE; +} + +/* + * 生成されたQRコードシンボルを fmt で指定した形式に変換する + */ +QR_API qr_byte_t * +qrGetSymbol(QRCode *qr, int fmt, int sep, int mag, int *size) +{ + qr_byte_t *buf; + int _size; + + static const QRConverter cnv[QR_FMT_COUNT] = { + qrSymbolToBMP, + qrSymbolToPBM, + qrSymbolToJSON, + qrSymbolToDigit, + qrSymbolToASCII + }; + + if (fmt < 0 || fmt >= QR_FMT_COUNT) { + qrSetErrorInfo(qr, QR_ERR_INVALID_FMT, NULL); + return NULL; + } + + if (cnv[fmt] == NULL) { + qrSetErrorInfo(qr, QR_ERR_UNSUPPORTED_FMT, NULL); + return NULL; + } + + buf = cnv[fmt](qr, sep, mag, &_size); + if (buf == NULL) { + return NULL; + } + + if (size) { + *size = _size; + } + return buf; +} + +/* + * 生成されたQRコードシンボルをストリーム fp に書き込む + */ +QR_API int +qrOutputSymbol(QRCode *qr, FILE *fp, int fmt, int sep, int mag) +{ + qr_byte_t *buf; + int size; + + buf = qrGetSymbol(qr, fmt, sep, mag, &size); + if (buf == NULL) { + return -1; + } + + if (fp == NULL) { + fp = stdout; + } + + if (!fwrite(buf, (size_t)size, 1, fp)) { + qrSetErrorInfo2(qr, QR_ERR_FWRITE, NULL); + free(buf); + return -1; + } + if (ferror(fp)) { + qrSetErrorInfo(qr, QR_ERR_FWRITE, NULL); + free(buf); + return -1; + } + + free(buf); + + return size; +} + +/* + * 生成されたQRコードシンボルをファイル pathname に書き込む + */ +QR_API int +qrOutputSymbol2(QRCode *qr, const char *pathname, int fmt, int sep, int mag) +{ + FILE *fp; + int size; + + if (pathname == NULL || pathname[0] == '\0') { + qrSetErrorInfo(qr, QR_ERR_EMPTY_PARAM, "(empty pathname)"); + return -1; + } + + fp = fopen(pathname, "wb"); + if (fp == NULL) { + qrSetErrorInfo2(qr, QR_ERR_FOPEN, pathname); + return -1; + } + + size = qrOutputSymbol(qr, fp, fmt, sep, mag); + fclose(fp); + + return size; +} + +/* + * 生成されたQRコードシンボルすべてを fmt で指定した形式に変換する + */ +QR_API qr_byte_t * +qrsGetSymbols(QRStructured *st, int fmt, int sep, int mag, int order, int *size) +{ + qr_byte_t *buf; + int _size; + + static QRsConverter cnv[QR_FMT_COUNT] = { + qrsSymbolsToPBM, + qrsSymbolsToJSON, + qrsSymbolsToDigit, + qrsSymbolsToASCII + }; + + if (fmt < 0 || fmt >= QR_FMT_COUNT) { + qrSetErrorInfo(st->cur, QR_ERR_INVALID_FMT, NULL); + return NULL; + } + + buf = cnv[fmt](st, sep, mag, order, &_size); + if (buf == NULL) { + return NULL; + } + + if (size) { + *size = _size; + } + + return buf; +} + +/* + * 生成されたQRコードシンボルすべてをストリーム fp に書き込む + */ +QR_API int +qrsOutputSymbols(QRStructured *st, FILE *fp, int fmt, int sep, int mag, int order) +{ + qr_byte_t *buf; + int size; + + + buf = qrsGetSymbols(st, fmt, sep, mag, order, &size); + if (buf == NULL) { + return -1; + } + + if (fp == NULL) { + fp = stdout; + } + + if (!fwrite(buf, (size_t)size, 1, fp)) { + qrSetErrorInfo2(st->cur, QR_ERR_FWRITE, NULL); + free(buf); + return -1; + } + if (ferror(fp)) { + qrSetErrorInfo(st->cur, QR_ERR_FWRITE, NULL); + free(buf); + return -1; + } + + free(buf); + + return size; +} + +/* + * 生成されたQRコードシンボルすべてをファイル pathname に書き込む + */ +QR_API int +qrsOutputSymbols2(QRStructured *st, const char *pathname, int fmt, int sep, int mag, int order) +{ + FILE *fp; + int size; + + if (pathname == NULL || pathname[0] == '\0') { + qrSetErrorInfo(st->cur, QR_ERR_EMPTY_PARAM, "(empty pathname)"); + return -1; + } + + fp = fopen(pathname, "wb"); + if (fp == NULL) { + qrSetErrorInfo2(st->cur, QR_ERR_FOPEN, pathname); + return -1; + } + + size = qrsOutputSymbols(st, fp, fmt, sep, mag, order); + fclose(fp); + + return size; +} diff --git a/kvm_system/main/lib/libqr/qr.h b/kvm_system/main/lib/libqr/qr.h new file mode 100644 index 0000000..657ad09 --- /dev/null +++ b/kvm_system/main/lib/libqr/qr.h @@ -0,0 +1,341 @@ +/* + * QR Code Generator Library: Basic Header + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#ifndef _QR_H_ +#define _QR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#if defined(WIN32) && !defined(QR_STATIC_BUILD) +#ifdef QR_DLL_BUILD +#define QR_API __declspec(dllexport) +#else +#define QR_API __declspec(dllimport) +#endif +#else +#define QR_API +#endif + +/* + * ライブラリのバージョン + */ +#define LIBQR_VERSION "0.3.1" + +/* + * エラーコード + */ +typedef enum { + /* 汎用エラーコード */ + QR_ERR_NONE = 0, + QR_ERR_USAGE = 0x68, + QR_ERR_NOT_IMPL = 0x69, + QR_ERR_SEE_ERRNO = 0x6e, + QR_ERR_FOPEN = 0x6f, + QR_ERR_FREAD = 0x72, + QR_ERR_STATE = 0x73, + QR_ERR_UNKNOWN = 0x75, + QR_ERR_FWRITE = 0x77, + QR_ERR_MEMORY_EXHAUSTED = 0x78, + + /* パラメータ用エラーコード */ + QR_ERR_INVALID_ARG = 0x01, + QR_ERR_INVALID_VERSION = 0x02, + QR_ERR_INVALID_MODE = 0x03, + QR_ERR_INVALID_ECL = 0x04, + QR_ERR_INVALID_MPT = 0x05, + QR_ERR_INVALID_MAG = 0x06, + QR_ERR_INVALID_SEP = 0x07, + QR_ERR_INVALID_SIZE = 0x08, + QR_ERR_INVALID_FMT = 0x09, + QR_ERR_INVALID_OUT = 0x0a, + QR_ERR_INVALID_MAXNUM = 0x0b, + QR_ERR_UNSUPPORTED_FMT = 0x0c, + QR_ERR_EMPTY_PARAM = 0x0f, + + /* 入力データ用エラーコード */ + QR_ERR_EMPTY_SRC = 0x10, + QR_ERR_LARGE_SRC = 0x11, + QR_ERR_NOT_NUMERIC = 0x12, + QR_ERR_NOT_ALNUM = 0x13, + QR_ERR_NOT_KANJI = 0x14, + + /* 画像処理用エラーコード */ + QR_ERR_IMAGE_TOO_LARGE = 0x30, + QR_ERR_WIDTH_TOO_LARGE = 0x31, + QR_ERR_HEIGHT_TOO_LARGE = 0x32, + QR_ERR_IMAGECREATE = 0x33, + QR_ERR_IMAGEFORMAT = 0x34, + QR_ERR_IMAGEFRAME = 0x35, + + /* zlib用エラーコード */ + QR_ERR_DEFLATE = 0x40 +} qr_err_t; + +/* + * 内部状態 + */ +#define QR_STATE_BEGIN 0 +#define QR_STATE_SET 1 +#define QR_STATE_FINAL 2 + +/* + * 符号化モード + */ +typedef enum { + QR_EM_AUTO = -1, /* 自動選択 */ + QR_EM_NUMERIC = 0, /* 数字 */ + QR_EM_ALNUM = 1, /* 英数字: 0-9 A-Z SP $%*+-./: */ + QR_EM_8BIT = 2, /* 8ビットバイト */ + QR_EM_KANJI = 3 /* 漢字 */ +} qr_em_t; + +/* モード総数 */ +#define QR_EM_COUNT 4 + +/* + * 誤り訂正レベル + */ +typedef enum { + QR_ECL_L = 0, /* レベルL */ + QR_ECL_M = 1, /* レベルM */ + QR_ECL_Q = 2, /* レベルQ */ + QR_ECL_H = 3 /* レベルH */ +} qr_ecl_t; + +/* レベル総数 */ +#define QR_ECL_COUNT 4 + +/* + * 出力形式 + */ +typedef enum { + QR_FMT_PNG = 0, /* PNG */ + QR_FMT_BMP = 1, /* BMP */ + QR_FMT_TIFF = 2, /* TIFF */ + QR_FMT_PBM = 3, /* PBM */ + QR_FMT_SVG = 4, /* SVG */ + QR_FMT_JSON = 5, /* JSON */ + QR_FMT_DIGIT = 6, /* 文字列 */ + QR_FMT_ASCII = 7, /* アスキーアート */ + QR_FMT_UNAVAILABLE = -1 /* 利用不可 */ +} qr_format_t; + +/* 出力形式総数 */ +#define QR_FMT_COUNT 8 + +/* + * モジュール値のマスク + */ +#define QR_MM_DATA 0x01 /* 符号化データの黒モジュール */ +#define QR_MM_BLACK 0x02 /* 印字される黒モジュール */ +#define QR_MM_FUNC 0x04 /* 機能パターン領域(形式/型番情報を含む) */ + +/* + * 機能パターンの定数 + */ +#define QR_DIM_SEP 4 /* 分離パターンの幅 */ +#define QR_DIM_FINDER 7 /* 位置検出パターンの1辺の長さ */ +#define QR_DIM_ALIGN 5 /* 位置合わせパターンの1辺の長さ */ +#define QR_DIM_TIMING 6 /* タイミングパターンのオフセット位置 */ + +/* + * サイズ定数 + */ +#define QR_SRC_MAX 7089 /* 入力データの最大長 */ +#define QR_DIM_MAX 177 /* 1辺のモジュール数の最大値 */ +#define QR_VER_MAX 40 /* 型番の最大値 */ +#define QR_DWD_MAX 2956 /* データコード語の最大長(型番40/レベルL) */ +#define QR_ECW_MAX 2430 /* 誤り訂正コード語の最大長(型番40/レベルH) */ +#define QR_CWD_MAX 3706 /* コード語の最大長(型番40) */ +#define QR_RSD_MAX 123 /* RSブロックデータコード語の最大長 */ +#define QR_RSW_MAX 68 /* RSブロック誤り訂正コード語の最大長 */ +#define QR_RSB_MAX 2 /* RSブロック種別の最大数 */ +#define QR_MPT_MAX 8 /* マスクパターン種別総数 */ +#define QR_APL_MAX 7 /* 位置合わせパターン座標の最大数 */ +#define QR_FIN_MAX 15 /* 形式情報のビット数 */ +#define QR_VIN_MAX 18 /* 型番情報のビット数 */ +#define QR_MAG_MAX 16 /* ピクセル表示倍率の最大値 */ +#define QR_SEP_MAX 16 /* 分離パターン幅の最大値 */ +#define QR_ERR_MAX 1024 /* エラー情報の最大長 */ +#define QR_STA_MAX 16 /* 構造的連接(分割/連結)の最大数 */ +#define QR_STA_LEN 20 /* 構造的連接ヘッダのビット数 */ + +/* + * その他の定数 + */ +#define NAV 0 /* 不使用(not available) */ +#define PADWORD1 0xec /* 埋め草コード語1: 11101100 */ +#define PADWORD2 0x11 /* 埋め草コード語2: 00010001 */ +#define VERPOINT1 9 /* 文字数指示子のビット数が変わる直前の型番1 */ +#define VERPOINT2 26 /* 文字数指示子のビット数が変わる直前の型番2 */ + +/* + * 8bitバイナリデータ型 + */ +typedef unsigned char qr_byte_t; + +/* + * RSブロックごとの情報 + */ +typedef struct qr_rsblock_t { + int rsbnum; /* RSブロック数 */ + int totalwords; /* RSブロック総コード語数 */ + int datawords; /* RSブロックデータコード語数 */ + int ecnum; /* RSブロック誤り訂正数(不使用) */ +} qr_rsblock_t; + +/* + * 誤り訂正レベルごとの情報 + */ +typedef struct qr_eclevel_t { + int datawords; /* データコード語数(全RSブロック) */ + int capacity[QR_EM_COUNT]; /* 符号化モードごとのデータ容量 */ + int nrsb; /* RSブロックの種類(1または2) */ + qr_rsblock_t rsb[QR_RSB_MAX]; /* RSブロックごとの情報 */ +} qr_eclevel_t; + +/* + * 型番ごとの情報 + */ +typedef struct qr_vertable_t { + int version; /* 型番 */ + int dimension; /* 1辺のモジュール数 */ + int totalwords; /* 総コード語数 */ + int remainedbits; /* 剰余ビット数 */ + int nlen[QR_EM_COUNT]; /* 文字数指示子のビット数 */ + qr_eclevel_t ecl[QR_ECL_COUNT]; /* 誤り訂正レベルごとの情報 */ + int aplnum; /* 位置合わせパターン中心座標数 */ + int aploc[QR_APL_MAX]; /* 位置合わせパターン中心座標 */ +} qr_vertable_t; + +/* + * 座標データ型 + */ +typedef struct qr_coord_t { int ypos, xpos; } qr_coord_t; + +/* + * パラメータ構造体 + */ +typedef struct qr_param_t { + int version; /* 型番 */ + int mode; /* 符号化モード */ + int eclevel; /* 誤り訂正レベル */ + int masktype; /* マスクパターン種別 */ +} qr_param_t; + +/* + * QRコードオブジェクト + */ +typedef struct qrcode_t { + qr_byte_t *dataword; /* データコード語領域のアドレス */ + qr_byte_t *ecword; /* 誤り訂正コード語領域のアドレス */ + qr_byte_t *codeword; /* シンボル配置用コード語領域のアドレス */ + qr_byte_t *_symbol; /* シンボルデータ領域のアドレス */ + qr_byte_t **symbol; /* シンボルデータの各行頭のアドレスのポインタ */ + qr_byte_t *source; /* 入力データ領域のアドレス */ + size_t srcmax; /* 入力データ領域の最大容量 */ + size_t srclen; /* 入力データ領域の使用容量 */ + int enclen; /* データコード語の総ビット長 */ + int delta1, delta2; /* 型番自動選択の補助に使われるビット長差分 */ + int dwpos; /* データコード語の追加バイト位置 */ + int dwbit; /* データコード語の追加ビット位置 */ + int xpos, ypos; /* モジュールを配置する座標位置 */ + int xdir, ydir; /* モジュール配置の移動方向 */ + int state; /* 処理の進行状況 */ + int errcode; /* 最後に起こったエラーの番号 */ + char errinfo[QR_ERR_MAX]; /* 最後に起こったエラーの詳細 */ + qr_param_t param; /* 出力パラメータ */ +} QRCode; + +/* + * 構造的連接QRコードオブジェクト + */ +typedef struct qrcode_sa_t { + QRCode *qrs[QR_STA_MAX]; /* QRコードオブジェクトのポインタ配列 */ + QRCode *cur; /* 値を入力する対象のQRコードオブジェクト */ + int num; /* シンボル数 */ + int max; /* 最大シンボル数 */ + int parity; /* パリティ */ + int state; /* 処理の進行状況 */ + qr_param_t param; /* 出力パラメータ */ +} QRStructured; + +/* + * QRコード出力関数型 + */ +typedef qr_byte_t *(*QRConverter)(QRCode *, int, int, int *); +typedef qr_byte_t *(*QRsConverter)(QRStructured *, int, int, int, int *); + +#define qrIsBlacke(qr, i, j) (((qr)->symbol[(i)][(j)] & QR_MM_BLACK) != 0) + +/* + * 基本関数のプロトタイプ + */ +QR_API QRCode *qrInit(int version, int mode, int eclevel, int masktype, int *errcode); +QR_API void qrDestroy(QRCode *qr); +QR_API int qrGetErrorCode(QRCode *qr); +QR_API char *qrGetErrorInfo(QRCode *qr); +QR_API int qrAddData(QRCode *qr, const qr_byte_t *source, int size); +QR_API int qrAddData2(QRCode *qr, const qr_byte_t *source, int size, int mode); +QR_API int qrFinalize(QRCode *qr); +QR_API int qrIsFinalized(const QRCode *qr); +QR_API int qrHasData(const QRCode *qr); +QR_API QRCode *qrClone(const QRCode *qr, int *errcode); + +/* + * 構造的連接操作用関数のプロトタイプ + */ +QR_API QRStructured *qrsInit(int version, int mode, int eclevel, int masktype, int maxnum, int *errcode); +QR_API void qrsDestroy(QRStructured *st); +QR_API int qrsGetErrorCode(QRStructured *st); +QR_API char *qrsGetErrorInfo(QRStructured *st); +QR_API int qrsAddData(QRStructured *st, const qr_byte_t *source, int size); +QR_API int qrsAddData2(QRStructured *st, const qr_byte_t *source, int size, int mode); +QR_API int qrsFinalize(QRStructured *st); +QR_API int qrsIsFinalized(const QRStructured *st); +QR_API int qrsHasData(const QRStructured *st); +QR_API QRStructured *qrsClone(const QRStructured *st, int *errcode); + +/* + * 出力用関数のプロトタイプ + */ +QR_API int qrOutputSymbol(QRCode *qr, FILE *fp, int fmt, int sep, int mag); +QR_API int qrOutputSymbol2(QRCode *qr, const char *pathname, int fmt, int sep, int mag); +QR_API qr_byte_t *qrGetSymbol(QRCode *qr, int fmt, int sep, int mag, int *size); +QR_API qr_byte_t *qrSymbolToDigit(QRCode *qr, int sep, int mag, int *size); +QR_API qr_byte_t *qrSymbolToASCII(QRCode *qr, int sep, int mag, int *size); +QR_API qr_byte_t *qrSymbolToJSON(QRCode *qr, int sep, int mag, int *size); +QR_API qr_byte_t *qrSymbolToPBM(QRCode *qr, int sep, int mag, int *size); +QR_API qr_byte_t *qrSymbolToBMP(QRCode *qr, int sep, int mag, int *size); + +/* + * 構造的連接出力用関数のプロトタイプ + */ +QR_API int qrsOutputSymbols(QRStructured *st, FILE *fp, int fmt, int sep, int mag, int order); +QR_API int qrsOutputSymbols2(QRStructured *st, const char *pathname, int fmt, int sep, int mag, int order); +QR_API qr_byte_t *qrsGetSymbols(QRStructured *st, int fmt, int sep, int mag, int order, int *size); +QR_API qr_byte_t *qrsSymbolsToDigit(QRStructured *st, int sep, int mag, int order, int *size); +QR_API qr_byte_t *qrsSymbolsToASCII(QRStructured *st, int sep, int mag, int order, int *size); +QR_API qr_byte_t *qrsSymbolsToJSON(QRStructured *st, int sep, int mag, int order, int *size); +QR_API qr_byte_t *qrsSymbolsToPBM(QRStructured *st, int sep, int mag, int order, int *size); +QR_API qr_byte_t *qrsSymbolsToBMP(QRStructured *st, int sep, int mag, int order, int *size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _QR_H_ */ diff --git a/kvm_system/main/lib/libqr/qr_dwtable.h b/kvm_system/main/lib/libqr/qr_dwtable.h new file mode 100644 index 0000000..3289b23 --- /dev/null +++ b/kvm_system/main/lib/libqr/qr_dwtable.h @@ -0,0 +1,48 @@ +static const short qr_dwtable_kanji[42][189] = { +{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,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,-1,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,120,121,122,123,124,125,126,127,-1,-1,-1,-1,-1,-1,-1,-1,136,137,138,139,140,141,142,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,-1,-1,-1,-1,-1,-1,-1,176,177,178,179,180,181,182,183,-1,-1,-1,-1,188}, +{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,207,208,209,210,211,212,213,214,215,216,-1,-1,-1,-1,-1,-1,-1,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,-1,-1,-1,-1,-1,-1,-1,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,-1,-1,-1,-1,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,-1,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,-1,-1,-1,-1,-1,-1,-1,-1,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,-1,-1,-1,-1,-1,-1,-1,-1,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,-1,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +#ifdef QR_DISABLE_CP932_CHARACTER +{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +#else +{1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,-1,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,-1,-1,-1,-1,-1,-1,-1,-1,1214,-1,1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243,1244,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +#endif +{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1530,1531,1532}, +{1536,1537,1538,1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550,1551,1552,1553,1554,1555,1556,1557,1558,1559,1560,1561,1562,1563,1564,1565,1566,1567,1568,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1595,1596,1597,1598,-1,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,1629,1630,1631,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721,1722,1723,1724}, +{1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,-1,1792,1793,1794,1795,1796,1797,1798,1799,1800,1801,1802,1803,1804,1805,1806,1807,1808,1809,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916}, +{1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937,1938,1939,1940,1941,1942,1943,1944,1945,1946,1947,1948,1949,1950,1951,1952,1953,1954,1955,1956,1957,1958,1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,-1,1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048,2049,2050,2051,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071,2072,2073,2074,2075,2076,2077,2078,2079,2080,2081,2082,2083,2084,2085,2086,2087,2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108}, +{2112,2113,2114,2115,2116,2117,2118,2119,2120,2121,2122,2123,2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,2148,2149,2150,2151,2152,2153,2154,2155,2156,2157,2158,2159,2160,2161,2162,2163,2164,2165,2166,2167,2168,2169,2170,2171,2172,2173,2174,-1,2176,2177,2178,2179,2180,2181,2182,2183,2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198,2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209,2210,2211,2212,2213,2214,2215,2216,2217,2218,2219,2220,2221,2222,2223,2224,2225,2226,2227,2228,2229,2230,2231,2232,2233,2234,2235,2236,2237,2238,2239,2240,2241,2242,2243,2244,2245,2246,2247,2248,2249,2250,2251,2252,2253,2254,2255,2256,2257,2258,2259,2260,2261,2262,2263,2264,2265,2266,2267,2268,2269,2270,2271,2272,2273,2274,2275,2276,2277,2278,2279,2280,2281,2282,2283,2284,2285,2286,2287,2288,2289,2290,2291,2292,2293,2294,2295,2296,2297,2298,2299,2300}, +{2304,2305,2306,2307,2308,2309,2310,2311,2312,2313,2314,2315,2316,2317,2318,2319,2320,2321,2322,2323,2324,2325,2326,2327,2328,2329,2330,2331,2332,2333,2334,2335,2336,2337,2338,2339,2340,2341,2342,2343,2344,2345,2346,2347,2348,2349,2350,2351,2352,2353,2354,2355,2356,2357,2358,2359,2360,2361,2362,2363,2364,2365,2366,-1,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380,2381,2382,2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2395,2396,2397,2398,2399,2400,2401,2402,2403,2404,2405,2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,2420,2421,2422,2423,2424,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2435,2436,2437,2438,2439,2440,2441,2442,2443,2444,2445,2446,2447,2448,2449,2450,2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464,2465,2466,2467,2468,2469,2470,2471,2472,2473,2474,2475,2476,2477,2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490,2491,2492}, +{2496,2497,2498,2499,2500,2501,2502,2503,2504,2505,2506,2507,2508,2509,2510,2511,2512,2513,2514,2515,2516,2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2527,2528,2529,2530,2531,2532,2533,2534,2535,2536,2537,2538,2539,2540,2541,2542,2543,2544,2545,2546,2547,2548,2549,2550,2551,2552,2553,2554,2555,2556,2557,2558,-1,2560,2561,2562,2563,2564,2565,2566,2567,2568,2569,2570,2571,2572,2573,2574,2575,2576,2577,2578,2579,2580,2581,2582,2583,2584,2585,2586,2587,2588,2589,2590,2591,2592,2593,2594,2595,2596,2597,2598,2599,2600,2601,2602,2603,2604,2605,2606,2607,2608,2609,2610,2611,2612,2613,2614,2615,2616,2617,2618,2619,2620,2621,2622,2623,2624,2625,2626,2627,2628,2629,2630,2631,2632,2633,2634,2635,2636,2637,2638,2639,2640,2641,2642,2643,2644,2645,2646,2647,2648,2649,2650,2651,2652,2653,2654,2655,2656,2657,2658,2659,2660,2661,2662,2663,2664,2665,2666,2667,2668,2669,2670,2671,2672,2673,2674,2675,2676,2677,2678,2679,2680,2681,2682,2683,2684}, +{2688,2689,2690,2691,2692,2693,2694,2695,2696,2697,2698,2699,2700,2701,2702,2703,2704,2705,2706,2707,2708,2709,2710,2711,2712,2713,2714,2715,2716,2717,2718,2719,2720,2721,2722,2723,2724,2725,2726,2727,2728,2729,2730,2731,2732,2733,2734,2735,2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,-1,2752,2753,2754,2755,2756,2757,2758,2759,2760,2761,2762,2763,2764,2765,2766,2767,2768,2769,2770,2771,2772,2773,2774,2775,2776,2777,2778,2779,2780,2781,2782,2783,2784,2785,2786,2787,2788,2789,2790,2791,2792,2793,2794,2795,2796,2797,2798,2799,2800,2801,2802,2803,2804,2805,2806,2807,2808,2809,2810,2811,2812,2813,2814,2815,2816,2817,2818,2819,2820,2821,2822,2823,2824,2825,2826,2827,2828,2829,2830,2831,2832,2833,2834,2835,2836,2837,2838,2839,2840,2841,2842,2843,2844,2845,2846,2847,2848,2849,2850,2851,2852,2853,2854,2855,2856,2857,2858,2859,2860,2861,2862,2863,2864,2865,2866,2867,2868,2869,2870,2871,2872,2873,2874,2875,2876}, +{2880,2881,2882,2883,2884,2885,2886,2887,2888,2889,2890,2891,2892,2893,2894,2895,2896,2897,2898,2899,2900,2901,2902,2903,2904,2905,2906,2907,2908,2909,2910,2911,2912,2913,2914,2915,2916,2917,2918,2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2930,2931,2932,2933,2934,2935,2936,2937,2938,2939,2940,2941,2942,-1,2944,2945,2946,2947,2948,2949,2950,2951,2952,2953,2954,2955,2956,2957,2958,2959,2960,2961,2962,2963,2964,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976,2977,2978,2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2989,2990,2991,2992,2993,2994,2995,2996,2997,2998,2999,3000,3001,3002,3003,3004,3005,3006,3007,3008,3009,3010,3011,3012,3013,3014,3015,3016,3017,3018,3019,3020,3021,3022,3023,3024,3025,3026,3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037,3038,3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3050,3051,3052,3053,3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066,3067,3068}, +{3072,3073,3074,3075,3076,3077,3078,3079,3080,3081,3082,3083,3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095,3096,3097,3098,3099,3100,3101,3102,3103,3104,3105,3106,3107,3108,3109,3110,3111,3112,3113,3114,3115,3116,3117,3118,3119,3120,3121,3122,3123,3124,3125,3126,3127,3128,3129,3130,3131,3132,3133,3134,-1,3136,3137,3138,3139,3140,3141,3142,3143,3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156,3157,3158,3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172,3173,3174,3175,3176,3177,3178,3179,3180,3181,3182,3183,3184,3185,3186,3187,3188,3189,3190,3191,3192,3193,3194,3195,3196,3197,3198,3199,3200,3201,3202,3203,3204,3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217,3218,3219,3220,3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233,3234,3235,3236,3237,3238,3239,3240,3241,3242,3243,3244,3245,3246,3247,3248,3249,3250,3251,3252,3253,3254,3255,3256,3257,3258,3259,3260}, +{3264,3265,3266,3267,3268,3269,3270,3271,3272,3273,3274,3275,3276,3277,3278,3279,3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293,3294,3295,3296,3297,3298,3299,3300,3301,3302,3303,3304,3305,3306,3307,3308,3309,3310,3311,3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323,3324,3325,3326,-1,3328,3329,3330,3331,3332,3333,3334,3335,3336,3337,3338,3339,3340,3341,3342,3343,3344,3345,3346,3347,3348,3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359,3360,3361,3362,3363,3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387,3388,3389,3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403,3404,3405,3406,3407,3408,3409,3410,3411,3412,3413,3414,3415,3416,3417,3418,3419,3420,3421,3422,3423,3424,3425,3426,3427,3428,3429,3430,3431,3432,3433,3434,3435,3436,3437,3438,3439,3440,3441,3442,3443,3444,3445,3446,3447,3448,3449,3450,3451,3452}, +{3456,3457,3458,3459,3460,3461,3462,3463,3464,3465,3466,3467,3468,3469,3470,3471,3472,3473,3474,3475,3476,3477,3478,3479,3480,3481,3482,3483,3484,3485,3486,3487,3488,3489,3490,3491,3492,3493,3494,3495,3496,3497,3498,3499,3500,3501,3502,3503,3504,3505,3506,3507,3508,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,-1,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545,3546,3547,3548,3549,3550,3551,3552,3553,3554,3555,3556,3557,3558,3559,3560,3561,3562,3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3575,3576,3577,3578,3579,3580,3581,3582,3583,3584,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,3643,3644}, +{3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,-1,3712,3713,3714,3715,3716,3717,3718,3719,3720,3721,3722,3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738,3739,3740,3741,3742,3743,3744,3745,3746,3747,3748,3749,3750,3751,3752,3753,3754,3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784,3785,3786,3787,3788,3789,3790,3791,3792,3793,3794,3795,3796,3797,3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815,3816,3817,3818,3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3829,3830,3831,3832,3833,3834,3835,3836}, +{3840,3841,3842,3843,3844,3845,3846,3847,3848,3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862,3863,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877,3878,3879,3880,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893,3894,3895,3896,3897,3898,3899,3900,3901,3902,-1,3904,3905,3906,3907,3908,3909,3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,3920,3921,3922,3923,3924,3925,3926,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943,3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990,3991,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005,4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022,4023,4024,4025,4026,4027,4028}, +{4032,4033,4034,4035,4036,4037,4038,4039,4040,4041,4042,4043,4044,4045,4046,4047,4048,4049,4050,4051,4052,4053,4054,4055,4056,4057,4058,4059,4060,4061,4062,4063,4064,4065,4066,4067,4068,4069,4070,4071,4072,4073,4074,4075,4076,4077,4078,4079,4080,4081,4082,4083,4084,4085,4086,4087,4088,4089,4090,4091,4092,4093,4094,-1,4096,4097,4098,4099,4100,4101,4102,4103,4104,4105,4106,4107,4108,4109,4110,4111,4112,4113,4114,4115,4116,4117,4118,4119,4120,4121,4122,4123,4124,4125,4126,4127,4128,4129,4130,4131,4132,4133,4134,4135,4136,4137,4138,4139,4140,4141,4142,4143,4144,4145,4146,4147,4148,4149,4150,4151,4152,4153,4154,4155,4156,4157,4158,4159,4160,4161,4162,4163,4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4174,4175,4176,4177,4178,4179,4180,4181,4182,4183,4184,4185,4186,4187,4188,4189,4190,4191,4192,4193,4194,4195,4196,4197,4198,4199,4200,4201,4202,4203,4204,4205,4206,4207,4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,4220}, +{4224,4225,4226,4227,4228,4229,4230,4231,4232,4233,4234,4235,4236,4237,4238,4239,4240,4241,4242,4243,4244,4245,4246,4247,4248,4249,4250,4251,4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265,4266,4267,4268,4269,4270,4271,4272,4273,4274,4275,4276,4277,4278,4279,4280,4281,4282,4283,4284,4285,4286,-1,4288,4289,4290,4291,4292,4293,4294,4295,4296,4297,4298,4299,4300,4301,4302,4303,4304,4305,4306,4307,4308,4309,4310,4311,4312,4313,4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,4326,4327,4328,4329,4330,4331,4332,4333,4334,4335,4336,4337,4338,4339,4340,4341,4342,4343,4344,4345,4346,4347,4348,4349,4350,4351,4352,4353,4354,4355,4356,4357,4358,4359,4360,4361,4362,4363,4364,4365,4366,4367,4368,4369,4370,4371,4372,4373,4374,4375,4376,4377,4378,4379,4380,4381,4382,4383,4384,4385,4386,4387,4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403,4404,4405,4406,4407,4408,4409,4410,4411,4412}, +{4416,4417,4418,4419,4420,4421,4422,4423,4424,4425,4426,4427,4428,4429,4430,4431,4432,4433,4434,4435,4436,4437,4438,4439,4440,4441,4442,4443,4444,4445,4446,4447,4448,4449,4450,4451,4452,4453,4454,4455,4456,4457,4458,4459,4460,4461,4462,4463,4464,4465,4466,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,4511,4512,4513,4514,4515,4516,4517,4518,4519,4520,4521,4522,4523,4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,4536,4537,4538,4539,4540,4541,4542,4543,4544,4545,4546,4547,4548,4549,4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567,4568,4569,4570,4571,4572,4573,4574,4575,4576,4577,4578,4579,4580,4581,4582,4583,4584,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594,4595,4596,4597,4598,4599,4600,4601,4602,4603,4604}, +{4608,4609,4610,4611,4612,4613,4614,4615,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626,4627,4628,4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642,4643,4644,4645,4646,4647,4648,4649,4650,4651,4652,4653,4654,4655,4656,4657,4658,4659,4660,4661,4662,4663,4664,4665,4666,4667,4668,4669,4670,-1,4672,4673,4674,4675,4676,4677,4678,4679,4680,4681,4682,4683,4684,4685,4686,4687,4688,4689,4690,4691,4692,4693,4694,4695,4696,4697,4698,4699,4700,4701,4702,4703,4704,4705,4706,4707,4708,4709,4710,4711,4712,4713,4714,4715,4716,4717,4718,4719,4720,4721,4722,4723,4724,4725,4726,4727,4728,4729,4730,4731,4732,4733,4734,4735,4736,4737,4738,4739,4740,4741,4742,4743,4744,4745,4746,4747,4748,4749,4750,4751,4752,4753,4754,4755,4756,4757,4758,4759,4760,4761,4762,4763,4764,4765,4766,4767,4768,4769,4770,4771,4772,4773,4774,4775,4776,4777,4778,4779,4780,4781,4782,4783,4784,4785,4786,4787,4788,4789,4790,4791,4792,4793,4794,4795,4796}, +{4800,4801,4802,4803,4804,4805,4806,4807,4808,4809,4810,4811,4812,4813,4814,4815,4816,4817,4818,4819,4820,4821,4822,4823,4824,4825,4826,4827,4828,4829,4830,4831,4832,4833,4834,4835,4836,4837,4838,4839,4840,4841,4842,4843,4844,4845,4846,4847,4848,4849,4850,4851,4852,4853,4854,4855,4856,4857,4858,4859,4860,4861,4862,-1,4864,4865,4866,4867,4868,4869,4870,4871,4872,4873,4874,4875,4876,4877,4878,4879,4880,4881,4882,4883,4884,4885,4886,4887,4888,4889,4890,4891,4892,4893,4894,4895,4896,4897,4898,4899,4900,4901,4902,4903,4904,4905,4906,4907,4908,4909,4910,4911,4912,4913,4914,4915,4916,4917,4918,4919,4920,4921,4922,4923,4924,4925,4926,4927,4928,4929,4930,4931,4932,4933,4934,4935,4936,4937,4938,4939,4940,4941,4942,4943,4944,4945,4946,4947,4948,4949,4950,4951,4952,4953,4954,4955,4956,4957,4958,4959,4960,4961,4962,4963,4964,4965,4966,4967,4968,4969,4970,4971,4972,4973,4974,4975,4976,4977,4978,4979,4980,4981,4982,4983,4984,4985,4986,4987,4988}, +{4992,4993,4994,4995,4996,4997,4998,4999,5000,5001,5002,5003,5004,5005,5006,5007,5008,5009,5010,5011,5012,5013,5014,5015,5016,5017,5018,5019,5020,5021,5022,5023,5024,5025,5026,5027,5028,5029,5030,5031,5032,5033,5034,5035,5036,5037,5038,5039,5040,5041,5042,5043,5044,5045,5046,5047,5048,5049,5050,5051,5052,5053,5054,-1,5056,5057,5058,5059,5060,5061,5062,5063,5064,5065,5066,5067,5068,5069,5070,5071,5072,5073,5074,5075,5076,5077,5078,5079,5080,5081,5082,5083,5084,5085,5086,5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102,5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,5113,5114,5115,5116,5117,5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133,5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149,5150,5151,5152,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164,5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,5176,5177,5178,5179,5180}, +{5184,5185,5186,5187,5188,5189,5190,5191,5192,5193,5194,5195,5196,5197,5198,5199,5200,5201,5202,5203,5204,5205,5206,5207,5208,5209,5210,5211,5212,5213,5214,5215,5216,5217,5218,5219,5220,5221,5222,5223,5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,5237,5238,5239,5240,5241,5242,5243,5244,5245,5246,-1,5248,5249,5250,5251,5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267,5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283,5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331,5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347,5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,5364,5365,5366,5367,5368,5369,5370,5371,5372}, +{5376,5377,5378,5379,5380,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391,5392,5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408,5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424,5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,-1,5440,5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456,5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488,5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504,5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520,5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536,5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552,5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564}, +{5568,5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584,5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600,5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616,5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,-1,5632,5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648,5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664,5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680,5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696,5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712,5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728,5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744,5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756}, +{5760,5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776,5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792,5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808,5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,-1,5824,5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856,5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872,5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888,5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904,5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920,5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936,5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948}, +{5952,5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968,5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984,5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,-1,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,6114,6115,6116,6117,6118,6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,6132,6133,6134,6135,6136,6137,6138,6139,6140}, +{6144,6145,6146,6147,6148,6149,6150,6151,6152,6153,6154,6155,6156,6157,6158,6159,6160,6161,6162,6163,6164,6165,6166,6167,6168,6169,6170,6171,6172,6173,6174,6175,6176,6177,6178,6179,6180,6181,6182,6183,6184,6185,6186,6187,6188,6189,6190,6191,6192,6193,6194,6195,6196,6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,-1,6208,6209,6210,6211,6212,6213,6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229,6230,6231,6232,6233,6234,6235,6236,6237,6238,6239,6240,6241,6242,6243,6244,6245,6246,6247,6248,6249,6250,6251,6252,6253,6254,6255,6256,6257,6258,6259,6260,6261,6262,6263,6264,6265,6266,6267,6268,6269,6270,6271,6272,6273,6274,6275,6276,6277,6278,6279,6280,6281,6282,6283,6284,6285,6286,6287,6288,6289,6290,6291,6292,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,6303,6304,6305,6306,6307,6308,6309,6310,6311,6312,6313,6314,6315,6316,6317,6318,6319,6320,6321,6322,6323,6324,6325,6326,6327,6328,6329,6330,6331,6332}, +{6336,6337,6338,6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349,6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363,6364,6365,6366,6367,6368,6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379,6380,6381,6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395,6396,6397,6398,-1,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,6411,6412,6413,6414,6415,6416,6417,6418,6419,6420,6421,6422,6423,6424,6425,6426,6427,6428,6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,6439,6440,6441,6442,6443,6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,6455,6456,6457,6458,6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472,6473,6474,6475,6476,6477,6478,6479,6480,6481,6482,6483,6484,6485,6486,6487,6488,6489,6490,6491,6492,6493,6494,6495,6496,6497,6498,6499,6500,6501,6502,6503,6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516,6517,6518,6519,6520,6521,6522,6523,6524}, +{6528,6529,6530,6531,6532,6533,6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548,6549,6550,6551,6552,6553,6554,6555,6556,6557,6558,6559,6560,6561,6562,6563,6564,6565,6566,6567,6568,6569,6570,6571,6572,6573,6574,6575,6576,6577,6578,6579,6580,6581,6582,6583,6584,6585,6586,6587,6588,6589,6590,-1,6592,6593,6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604,6605,6606,6607,6608,6609,6610,6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,6622,6623,6624,6625,6626,6627,6628,6629,6630,6631,6632,6633,6634,6635,6636,6637,6638,6639,6640,6641,6642,6643,6644,6645,6646,6647,6648,6649,6650,6651,6652,6653,6654,6655,6656,6657,6658,6659,6660,6661,6662,6663,6664,6665,6666,6667,6668,6669,6670,6671,6672,6673,6674,6675,6676,6677,6678,6679,6680,6681,6682,6683,6684,6685,6686,6687,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,6698,6699,6700,6701,6702,6703,6704,6705,6706,6707,6708,6709,6710,6711,6712,6713,6714,6715,6716}, +{6720,6721,6722,6723,6724,6725,6726,6727,6728,6729,6730,6731,6732,6733,6734,6735,6736,6737,6738,6739,6740,6741,6742,6743,6744,6745,6746,6747,6748,6749,6750,6751,6752,6753,6754,6755,6756,6757,6758,6759,6760,6761,6762,6763,6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,6774,6775,6776,6777,6778,6779,6780,6781,6782,-1,6784,6785,6786,6787,6788,6789,6790,6791,6792,6793,6794,6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806,6807,6808,6809,6810,6811,6812,6813,6814,6815,6816,6817,6818,6819,6820,6821,6822,6823,6824,6825,6826,6827,6828,6829,6830,6831,6832,6833,6834,6835,6836,6837,6838,6839,6840,6841,6842,6843,6844,6845,6846,6847,6848,6849,6850,6851,6852,6853,6854,6855,6856,6857,6858,6859,6860,6861,6862,6863,6864,6865,6866,6867,6868,6869,6870,6871,6872,6873,6874,6875,6876,6877,6878,6879,6880,6881,6882,6883,6884,6885,6886,6887,6888,6889,6890,6891,6892,6893,6894,6895,6896,6897,6898,6899,6900,6901,6902,6903,6904,6905,6906,6907,6908}, +{6912,6913,6914,6915,6916,6917,6918,6919,6920,6921,6922,6923,6924,6925,6926,6927,6928,6929,6930,6931,6932,6933,6934,6935,6936,6937,6938,6939,6940,6941,6942,6943,6944,6945,6946,6947,6948,6949,6950,6951,6952,6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968,6969,6970,6971,6972,6973,6974,-1,6976,6977,6978,6979,6980,6981,6982,6983,6984,6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,6998,6999,7000,7001,7002,7003,7004,7005,7006,7007,7008,7009,7010,7011,7012,7013,7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027,7028,7029,7030,7031,7032,7033,7034,7035,7036,7037,7038,7039,7040,7041,7042,7043,7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058,7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7073,7074,7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7086,7087,7088,7089,7090,7091,7092,7093,7094,7095,7096,7097,7098,7099,7100}, +{7104,7105,7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,7119,7120,7121,7122,7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,7134,7135,7136,7137,7138,7139,7140,7141,7142,7143,7144,7145,7146,7147,7148,7149,7150,7151,7152,7153,7154,7155,7156,7157,7158,7159,7160,7161,7162,7163,7164,7165,7166,-1,7168,7169,7170,7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183,7184,7185,7186,7187,7188,7189,7190,7191,7192,7193,7194,7195,7196,7197,7198,7199,7200,7201,7202,7203,7204,7205,7206,7207,7208,7209,7210,7211,7212,7213,7214,7215,7216,7217,7218,7219,7220,7221,7222,7223,7224,7225,7226,7227,7228,7229,7230,7231,7232,7233,7234,7235,7236,7237,7238,7239,7240,7241,7242,7243,7244,7245,7246,7247,7248,7249,7250,7251,7252,7253,7254,7255,7256,7257,7258,7259,7260,7261,7262,7263,7264,7265,7266,7267,7268,7269,7270,7271,7272,7273,7274,7275,7276,7277,7278,7279,7280,7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292}, +{7296,7297,7298,7299,7300,7301,7302,7303,7304,7305,7306,7307,7308,7309,7310,7311,7312,7313,7314,7315,7316,7317,7318,7319,7320,7321,7322,7323,7324,7325,7326,7327,7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339,7340,7341,7342,7343,7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,7354,7355,7356,7357,7358,-1,7360,7361,7362,7363,7364,7365,7366,7367,7368,7369,7370,7371,7372,7373,7374,7375,7376,7377,7378,7379,7380,7381,7382,7383,7384,7385,7386,7387,7388,7389,7390,7391,7392,7393,7394,7395,7396,7397,7398,7399,7400,7401,7402,7403,7404,7405,7406,7407,7408,7409,7410,7411,7412,7413,7414,7415,7416,7417,7418,7419,7420,7421,7422,7423,7424,7425,7426,7427,7428,7429,7430,7431,7432,7433,7434,7435,7436,7437,7438,7439,7440,7441,7442,7443,7444,7445,7446,7447,7448,7449,7450,7451,7452,7453,7454,7455,7456,7457,7458,7459,7460,7461,7462,7463,7464,7465,7466,7467,7468,7469,7470,7471,7472,7473,7474,7475,7476,7477,7478,7479,7480,7481,7482,7483,7484}, +{7488,7489,7490,7491,7492,7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,7503,7504,7505,7506,7507,7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519,7520,7521,7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535,7536,7537,7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,7550,-1,7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,7566,7567,7568,7569,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,7580,7581,7582,7583,7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7598,7599,7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,7610,7611,7612,7613,7614,7615,7616,7617,7618,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628,7629,7630,7631,7632,7633,7634,7635,7636,7637,7638,7639,7640,7641,7642,7643,7644,7645,7646,7647,7648,7649,7650,7651,7652,7653,7654,7655,7656,7657,7658,7659,7660,7661,7662,7663,7664,7665,7666,7667,7668,7669,7670,7671,7672,7673,7674,7675,7676}, +{7680,7681,7682,7683,7684,7685,7686,7687,7688,7689,7690,7691,7692,7693,7694,7695,7696,7697,7698,7699,7700,7701,7702,7703,7704,7705,7706,7707,7708,7709,7710,7711,7712,7713,7714,7715,7716,7717,7718,7719,7720,7721,7722,7723,7724,7725,7726,7727,7728,7729,7730,7731,7732,7733,7734,7735,7736,7737,7738,7739,7740,7741,7742,-1,7744,7745,7746,7747,7748,7749,7750,7751,7752,7753,7754,7755,7756,7757,7758,7759,7760,7761,7762,7763,7764,7765,7766,7767,7768,7769,7770,7771,7772,7773,7774,7775,7776,7777,7778,7779,7780,7781,7782,7783,7784,7785,7786,7787,7788,7789,7790,7791,7792,7793,7794,7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,7806,7807,7808,7809,7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823,7824,7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839,7840,7841,7842,7843,7844,7845,7846,7847,7848,7849,7850,7851,7852,7853,7854,7855,7856,7857,7858,7859,7860,7861,7862,7863,7864,7865,7866,7867,7868}, +{7872,7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886,7887,7888,7889,7890,7891,7892,7893,7894,7895,7896,7897,7898,7899,7900,7901,7902,7903,7904,7905,7906,7907,7908,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7919,7920,7921,7922,7923,7924,7925,7926,7927,7928,7929,7930,7931,7932,7933,7934,-1,7936,7937,7938,7939,7940,7941,7942,7943,7944,7945,7946,7947,7948,7949,7950,7951,7952,7953,7954,7955,7956,7957,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967,7968,7969,7970,7971,7972,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} +}; diff --git a/kvm_system/main/lib/libqr/qr_private.h b/kvm_system/main/lib/libqr/qr_private.h new file mode 100644 index 0000000..ce38775 --- /dev/null +++ b/kvm_system/main/lib/libqr/qr_private.h @@ -0,0 +1,512 @@ +/* + * QR Code Generator Library: Private Definitions + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#ifndef _QR_PRIVATE_H_ +#define _QR_PRIVATE_H_ + +#include "qr.h" +#include +#include + +/* + * Booblean + */ +#ifdef TRUE +#undef TRUE +#endif +#ifdef FALSE +#undef FALSE +#endif +#define TRUE 1 +#define FALSE 0 + +/* + * 型番データ表 + */ +const qr_vertable_t qr_vertable[QR_VER_MAX+1] = { + { NAV, NAV, NAV, NAV, { 0, 0, 0, 0 }, + {{ NAV, { 0, 0, 0, 0 }, NAV, {{ 0, 0, 0, 0 }}}, + { NAV, { 0, 0, 0, 0 }, NAV, {{ 0, 0, 0, 0 }}}, + { NAV, { 0, 0, 0, 0 }, NAV, {{ 0, 0, 0, 0 }}}, + { NAV, { 0, 0, 0, 0 }, NAV, {{ 0, 0, 0, 0 }}}}, + NAV, { NAV, NAV }}, + { 1, 21, 26, 0, { 10, 9, 8, 8 }, + {{ 19, { 41, 25, 17, 10 }, 1, {{ 1, 26, 19, 2 }}}, + { 16, { 34, 20, 14, 8 }, 1, {{ 1, 26, 16, 4 }}}, + { 13, { 27, 16, 11, 7 }, 1, {{ 1, 26, 13, 6 }}}, + { 9, { 17, 10, 7, 4 }, 1, {{ 1, 26, 9, 8 }}}}, + 0, { NAV, NAV }}, + { 2, 25, 44, 7, { 10, 9, 8, 8 }, + {{ 34, { 77, 47, 32, 20 }, 1, {{ 1, 44, 34, 4 }}}, + { 28, { 63, 38, 26, 16 }, 1, {{ 1, 44, 28, 8 }}}, + { 22, { 48, 29, 20, 12 }, 1, {{ 1, 44, 22, 11 }}}, + { 16, { 34, 20, 14, 8 }, 1, {{ 1, 44, 16, 14 }}}}, + 2, { 6, 18 }}, + { 3, 29, 70, 7, { 10, 9, 8, 8 }, + {{ 55, { 127, 77, 53, 32 }, 1, {{ 1, 70, 55, 7 }}}, + { 44, { 101, 61, 42, 26 }, 1, {{ 1, 70, 44, 13 }}}, + { 34, { 77, 47, 32, 20 }, 1, {{ 2, 35, 17, 9 }}}, + { 26, { 58, 35, 24, 15 }, 1, {{ 2, 35, 13, 11 }}}}, + 2, { 6, 22 }}, + { 4, 33, 100, 7, { 10, 9, 8, 8 }, + {{ 80, { 187, 114, 78, 48 }, 1, {{ 1, 100, 80, 10 }}}, + { 64, { 149, 90, 62, 38 }, 1, {{ 2, 50, 32, 9 }}}, + { 48, { 111, 67, 46, 28 }, 1, {{ 2, 50, 24, 13 }}}, + { 36, { 82, 50, 34, 21 }, 1, {{ 4, 25, 9, 8 }}}}, + 2, { 6, 26 }}, + { 5, 37, 134, 7, { 10, 9, 8, 8 }, + {{ 108, { 255, 154, 106, 65 }, 1, {{ 1, 134, 108, 13 }}}, + { 86, { 202, 122, 84, 52 }, 1, {{ 2, 67, 43, 12 }}}, + { 62, { 144, 87, 60, 37 }, 2, {{ 2, 33, 15, 9 }, { 2, 34, 16, 9 }}}, + { 46, { 106, 64, 44, 27 }, 2, {{ 2, 33, 11, 11 }, { 2, 34, 12, 11 }}}}, + 2, { 6, 30 }}, + { 6, 41, 172, 7, { 10, 9, 8, 8 }, + {{ 136, { 322, 195, 134, 82 }, 1, {{ 2, 86, 68, 9 }}}, + { 108, { 255, 154, 106, 65 }, 1, {{ 4, 43, 27, 8 }}}, + { 76, { 178, 108, 74, 45 }, 1, {{ 4, 43, 19, 12 }}}, + { 60, { 139, 84, 58, 36 }, 1, {{ 4, 43, 15, 14 }}}}, + 2, { 6, 34 }}, + { 7, 45, 196, 0, { 10, 9, 8, 8 }, + {{ 156, { 370, 224, 154, 95 }, 1, {{ 2, 98, 78, 10 }}}, + { 124, { 293, 178, 122, 75 }, 1, {{ 4, 49, 31, 9 }}}, + { 88, { 207, 125, 86, 53 }, 2, {{ 2, 32, 14, 9 }, { 4, 33, 15, 9 }}}, + { 66, { 154, 93, 64, 39 }, 2, {{ 4, 39, 13, 13 }, { 1, 40, 14, 13 }}}}, + 3, { 6, 22, 38 }}, + { 8, 49, 242, 0, { 10, 9, 8, 8 }, + {{ 194, { 461, 279, 192, 118 }, 1, {{ 2, 121, 97, 12 }}}, + { 154, { 365, 221, 152, 93 }, 2, {{ 2, 60, 38, 11 }, { 2, 61, 39, 11 }}}, + { 110, { 259, 157, 108, 66 }, 2, {{ 4, 40, 18, 11 }, { 2, 41, 19, 11 }}}, + { 86, { 202, 122, 84, 52 }, 2, {{ 4, 40, 14, 13 }, { 2, 41, 15, 13 }}}}, + 3, { 6, 24, 42 }}, + { 9, 53, 292, 0, { 10, 9, 8, 8 }, + {{ 232, { 552, 335, 230, 141 }, 1, {{ 2, 146, 116, 15 }}}, + { 182, { 432, 262, 180, 111 }, 2, {{ 3, 58, 36, 11 }, { 2, 59, 37, 11 }}}, + { 132, { 312, 189, 130, 80 }, 2, {{ 4, 36, 16, 10 }, { 4, 37, 17, 10 }}}, + { 100, { 235, 143, 98, 60 }, 2, {{ 4, 36, 12, 12 }, { 4, 37, 13, 12 }}}}, + 3, { 6, 26, 46 }}, + { 10, 57, 346, 0, { 12, 11, 16, 10 }, + {{ 274, { 652, 395, 271, 167 }, 2, {{ 2, 86, 68, 9 }, { 2, 87, 69, 9 }}}, + { 216, { 513, 311, 213, 131 }, 2, {{ 4, 69, 43, 13 }, { 1, 70, 44, 13 }}}, + { 154, { 364, 221, 151, 93 }, 2, {{ 6, 43, 19, 12 }, { 2, 44, 20, 12 }}}, + { 122, { 288, 174, 119, 74 }, 2, {{ 6, 43, 15, 14 }, { 2, 44, 16, 14 }}}}, + 3, { 6, 28, 50 }}, + { 11, 61, 404, 0, { 12, 11, 16, 10 }, + {{ 324, { 772, 468, 321, 198 }, 1, {{ 4, 101, 81, 10 }}}, + { 254, { 604, 366, 251, 155 }, 2, {{ 1, 80, 50, 15 }, { 4, 81, 51, 15 }}}, + { 180, { 427, 259, 177, 109 }, 2, {{ 4, 50, 22, 14 }, { 4, 51, 23, 14 }}}, + { 140, { 331, 200, 137, 85 }, 2, {{ 3, 36, 12, 12 }, { 8, 37, 13, 12 }}}}, + 3, { 6, 30, 54 }}, + { 12, 65, 466, 0, { 12, 11, 16, 10 }, + {{ 370, { 883, 535, 367, 226 }, 2, {{ 2, 116, 92, 12 }, { 2, 117, 93, 12 }}}, + { 290, { 691, 419, 287, 177 }, 2, {{ 6, 58, 36, 11 }, { 2, 59, 37, 11 }}}, + { 206, { 489, 296, 203, 125 }, 2, {{ 4, 46, 20, 13 }, { 6, 47, 21, 13 }}}, + { 158, { 374, 227, 155, 96 }, 2, {{ 7, 42, 14, 14 }, { 4, 43, 15, 14 }}}}, + 3, { 6, 32, 58 }}, + { 13, 69, 532, 0, { 12, 11, 16, 10 }, + {{ 428, { 1022, 619, 425, 262 }, 1, {{ 4, 133, 107, 13 }}}, + { 334, { 796, 483, 331, 204 }, 2, {{ 8, 59, 37, 11 }, { 1, 60, 38, 11 }}}, + { 244, { 580, 352, 241, 149 }, 2, {{ 8, 44, 20, 12 }, { 4, 45, 21, 12 }}}, + { 180, { 427, 259, 177, 109 }, 2, {{ 12, 33, 11, 11 }, { 4, 34, 12, 11 }}}}, + 3, { 6, 34, 62 }}, + { 14, 73, 581, 3, { 12, 11, 16, 10 }, + {{ 461, { 1101, 667, 458, 282 }, 2, {{ 3, 145, 115, 15 }, { 1, 146, 116, 15 }}}, + { 365, { 871, 528, 362, 223 }, 2, {{ 4, 64, 40, 12 }, { 5, 65, 41, 12 }}}, + { 261, { 621, 376, 258, 159 }, 2, {{ 11, 36, 16, 10 }, { 5, 37, 17, 10 }}}, + { 197, { 468, 283, 194, 120 }, 2, {{ 11, 36, 12, 12 }, { 5, 37, 13, 12 }}}}, + 4, { 6, 26, 46, 66 }}, + { 15, 77, 655, 3, { 12, 11, 16, 10 }, + {{ 523, { 1250, 758, 520, 320 }, 2, {{ 5, 109, 87, 11 }, { 1, 110, 88, 11 }}}, + { 415, { 991, 600, 412, 254 }, 2, {{ 5, 65, 41, 12 }, { 5, 66, 42, 12 }}}, + { 295, { 703, 426, 292, 180 }, 2, {{ 5, 54, 24, 15 }, { 7, 55, 25, 15 }}}, + { 223, { 530, 321, 220, 136 }, 2, {{ 11, 36, 12, 12 }, { 7, 37, 13, 12 }}}}, + 4, { 6, 26, 48, 70 }}, + { 16, 81, 733, 3, { 12, 11, 16, 10 }, + {{ 589, { 1408, 854, 586, 361 }, 2, {{ 5, 122, 98, 12 }, { 1, 123, 99, 12 }}}, + { 453, { 1082, 656, 450, 277 }, 2, {{ 7, 73, 45, 14 }, { 3, 74, 46, 14 }}}, + { 325, { 775, 470, 322, 198 }, 2, {{ 15, 43, 19, 12 }, { 2, 44, 20, 12 }}}, + { 253, { 602, 365, 250, 154 }, 2, {{ 3, 45, 15, 15 }, { 13, 46, 16, 15 }}}}, + 4, { 6, 26, 50, 74 }}, + { 17, 85, 815, 3, { 12, 11, 16, 10 }, + {{ 647, { 1548, 938, 644, 397 }, 2, {{ 1, 135, 107, 14 }, { 5, 136, 108, 14 }}}, + { 507, { 1212, 734, 504, 310 }, 2, {{ 10, 74, 46, 14 }, { 1, 75, 47, 14 }}}, + { 367, { 876, 531, 364, 224 }, 2, {{ 1, 50, 22, 14 }, { 15, 51, 23, 14 }}}, + { 283, { 674, 408, 280, 173 }, 2, {{ 2, 42, 14, 14 }, { 17, 43, 15, 14 }}}}, + 4, { 6, 30, 54, 78 }}, + { 18, 89, 901, 3, { 12, 11, 16, 10 }, + {{ 721, { 1725, 1046, 718, 442 }, 2, {{ 5, 150, 120, 15 }, { 1, 151, 121, 15 }}}, + { 563, { 1346, 816, 560, 345 }, 2, {{ 9, 69, 43, 13 }, { 4, 70, 44, 13 }}}, + { 397, { 948, 574, 394, 243 }, 2, {{ 17, 50, 22, 14 }, { 1, 51, 23, 14 }}}, + { 313, { 746, 452, 310, 191 }, 2, {{ 2, 42, 14, 14 }, { 19, 43, 15, 14 }}}}, + 4, { 6, 30, 56, 82 }}, + { 19, 93, 991, 3, { 12, 11, 16, 10 }, + {{ 795, { 1903, 1153, 792, 488 }, 2, {{ 3, 141, 113, 14 }, { 4, 142, 114, 14 }}}, + { 627, { 1500, 909, 624, 384 }, 2, {{ 3, 70, 44, 13 }, { 11, 71, 45, 13 }}}, + { 445, { 1063, 644, 442, 272 }, 2, {{ 17, 47, 21, 13 }, { 4, 48, 22, 13 }}}, + { 341, { 813, 493, 338, 208 }, 2, {{ 9, 39, 13, 13 }, { 16, 40, 14, 13 }}}}, + 4, { 6, 30, 58, 86 }}, + { 20, 97, 1085, 3, { 12, 11, 16, 10 }, + {{ 861, { 2061, 1249, 858, 528 }, 2, {{ 3, 135, 107, 14 }, { 5, 136, 108, 14 }}}, + { 669, { 1600, 970, 666, 410 }, 2, {{ 3, 67, 41, 13 }, { 13, 68, 42, 13 }}}, + { 485, { 1159, 702, 482, 297 }, 2, {{ 15, 54, 24, 15 }, { 5, 55, 25, 15 }}}, + { 385, { 919, 557, 382, 235 }, 2, {{ 15, 43, 15, 14 }, { 10, 44, 16, 14 }}}}, + 4, { 6, 34, 62, 90 }}, + { 21, 101, 1156, 4, { 12, 11, 16, 10 }, + {{ 932, { 2232, 1352, 929, 572 }, 2, {{ 4, 144, 116, 14 }, { 4, 145, 117, 14 }}}, + { 714, { 1708, 1035, 711, 438 }, 1, {{ 17, 68, 42, 13 }}}, + { 512, { 1224, 742, 509, 314 }, 2, {{ 17, 50, 22, 14 }, { 6, 51, 23, 14 }}}, + { 406, { 969, 587, 403, 248 }, 2, {{ 19, 46, 16, 15 }, { 6, 47, 17, 15 }}}}, + 5, { 6, 28, 50, 72, 94 }}, + { 22, 105, 1258, 4, { 12, 11, 16, 10 }, + {{ 1006, { 2409, 1460, 1003, 618 }, 2, {{ 2, 139, 111, 14 }, { 7, 140, 112, 14 }}}, + { 782, { 1872, 1134, 779, 480 }, 1, {{ 17, 74, 46, 14 }}}, + { 568, { 1358, 823, 565, 348 }, 2, {{ 7, 54, 24, 15 }, { 16, 55, 25, 15 }}}, + { 442, { 1056, 640, 439, 270 }, 1, {{ 34, 37, 13, 13 }}}}, + 5, { 6, 26, 50, 74, 98 }}, + { 23, 109, 1364, 4, { 12, 11, 16, 10 }, + {{ 1094, { 2620, 1588, 1091, 672 }, 2, {{ 4, 151, 121, 15 }, { 5, 152, 122, 15 }}}, + { 860, { 2059, 1248, 857, 528 }, 2, {{ 4, 75, 47, 14 }, { 14, 76, 48, 14 }}}, + { 614, { 1468, 890, 611, 376 }, 2, {{ 11, 54, 24, 15 }, { 14, 55, 25, 15 }}}, + { 464, { 1108, 672, 461, 284 }, 2, {{ 16, 45, 15, 15 }, { 14, 46, 16, 15 }}}}, + 5, { 6, 30, 54, 78, 102 }}, + { 24, 113, 1474, 4, { 12, 11, 16, 10 }, + {{ 1174, { 2812, 1704, 1171, 721 }, 2, {{ 6, 147, 117, 15 }, { 4, 148, 118, 15 }}}, + { 914, { 2188, 1326, 911, 561 }, 2, {{ 6, 73, 45, 14 }, { 14, 74, 46, 14 }}}, + { 664, { 1588, 963, 661, 407 }, 2, {{ 11, 54, 24, 15 }, { 16, 55, 25, 15 }}}, + { 514, { 1228, 744, 511, 315 }, 2, {{ 30, 46, 16, 15 }, { 2, 47, 17, 15 }}}}, + 5, { 6, 28, 54, 80, 106 }}, + { 25, 117, 1588, 4, { 12, 11, 16, 10 }, + {{ 1276, { 3057, 1853, 1273, 784 }, 2, {{ 8, 132, 106, 13 }, { 4, 133, 107, 13 }}}, + { 1000, { 2395, 1451, 997, 614 }, 2, {{ 8, 75, 47, 14 }, { 13, 76, 48, 14 }}}, + { 718, { 1718, 1041, 715, 440 }, 2, {{ 7, 54, 24, 15 }, { 22, 55, 25, 15 }}}, + { 538, { 1286, 779, 535, 330 }, 2, {{ 22, 45, 15, 15 }, { 13, 46, 16, 15 }}}}, + 5, { 6, 32, 58, 84, 110 }}, + { 26, 121, 1706, 4, { 12, 11, 16, 10 }, + {{ 1370, { 3283, 1990, 1367, 842 }, 2, {{ 10, 142, 114, 14 }, { 2, 143, 115, 14 }}}, + { 1062, { 2544, 1542, 1059, 652 }, 2, {{ 19, 74, 46, 14 }, { 4, 75, 47, 14 }}}, + { 754, { 1804, 1094, 751, 462 }, 2, {{ 28, 50, 22, 14 }, { 6, 51, 23, 14 }}}, + { 596, { 1425, 864, 593, 365 }, 2, {{ 33, 46, 16, 15 }, { 4, 47, 17, 15 }}}}, + 5, { 6, 30, 58, 86, 114 }}, + { 27, 125, 1828, 4, { 14, 13, 16, 12 }, + {{ 1468, { 3517, 2132, 1465, 902 }, 2, {{ 8, 152, 122, 15 }, { 4, 153, 123, 15 }}}, + { 1128, { 2701, 1637, 1125, 692 }, 2, {{ 22, 73, 45, 14 }, { 3, 74, 46, 14 }}}, + { 808, { 1933, 1172, 805, 496 }, 2, {{ 8, 53, 23, 15 }, { 26, 54, 24, 15 }}}, + { 628, { 1501, 910, 625, 385 }, 2, {{ 12, 45, 15, 15 }, { 28, 46, 16, 15 }}}}, + 5, { 6, 34, 62, 90, 118 }}, + { 28, 129, 1921, 3, { 14, 13, 16, 12 }, + {{ 1531, { 3669, 2223, 1528, 940 }, 2, {{ 3, 147, 117, 15 }, { 10, 148, 118, 15 }}}, + { 1193, { 2857, 1732, 1190, 732 }, 2, {{ 3, 73, 45, 14 }, { 23, 74, 46, 14 }}}, + { 871, { 2085, 1263, 868, 534 }, 2, {{ 4, 54, 24, 15 }, { 31, 55, 25, 15 }}}, + { 661, { 1581, 958, 658, 405 }, 2, {{ 11, 45, 15, 15 }, { 31, 46, 16, 15 }}}}, + 6, { 6, 26, 50, 74, 98, 122 }}, + { 29, 133, 2051, 3, { 14, 13, 16, 12 }, + {{ 1631, { 3909, 2369, 1628, 1002 }, 2, {{ 7, 146, 116, 15 }, { 7, 147, 117, 15 }}}, + { 1267, { 3035, 1839, 1264, 778 }, 2, {{ 21, 73, 45, 14 }, { 7, 74, 46, 14 }}}, + { 911, { 2181, 1322, 908, 559 }, 2, {{ 1, 53, 23, 15 }, { 37, 54, 24, 15 }}}, + { 701, { 1677, 1016, 698, 430 }, 2, {{ 19, 45, 15, 15 }, { 26, 46, 16, 15 }}}}, + 6, { 6, 30, 54, 78, 102, 126 }}, + { 30, 137, 2185, 3, { 14, 13, 16, 12 }, + {{ 1735, { 4158, 2520, 1732, 1066 }, 2, {{ 5, 145, 115, 15 }, { 10, 146, 116, 15 }}}, + { 1373, { 3289, 1994, 1370, 843 }, 2, {{ 19, 75, 47, 14 }, { 10, 76, 48, 14 }}}, + { 985, { 2358, 1429, 982, 604 }, 2, {{ 15, 54, 24, 15 }, { 25, 55, 25, 15 }}}, + { 745, { 1782, 1080, 742, 457 }, 2, {{ 23, 45, 15, 15 }, { 25, 46, 16, 15 }}}}, + 6, { 6, 26, 52, 78, 104, 130 }}, + { 31, 141, 2323, 3, { 14, 13, 16, 12 }, + {{ 1843, { 4417, 2677, 1840, 1132 }, 2, {{ 13, 145, 115, 15 }, { 3, 146, 116, 15 }}}, + { 1455, { 3486, 2113, 1452, 894 }, 2, {{ 2, 74, 46, 14 }, { 29, 75, 47, 14 }}}, + { 1033, { 2473, 1499, 1030, 634 }, 2, {{ 42, 54, 24, 15 }, { 1, 55, 25, 15 }}}, + { 793, { 1897, 1150, 790, 486 }, 2, {{ 23, 45, 15, 15 }, { 28, 46, 16, 15 }}}}, + 6, { 6, 30, 56, 82, 108, 134 }}, + { 32, 145, 2465, 3, { 14, 13, 16, 12 }, + {{ 1955, { 4686, 2840, 1952, 1201 }, 1, {{ 17, 145, 115, 15 }}}, + { 1541, { 3693, 2238, 1538, 947 }, 2, {{ 10, 74, 46, 14 }, { 23, 75, 47, 14 }}}, + { 1115, { 2670, 1618, 1112, 684 }, 2, {{ 10, 54, 24, 15 }, { 35, 55, 25, 15 }}}, + { 845, { 2022, 1226, 842, 518 }, 2, {{ 19, 45, 15, 15 }, { 35, 46, 16, 15 }}}}, + 6, { 6, 34, 60, 86, 112, 138 }}, + { 33, 149, 2611, 3, { 14, 13, 16, 12 }, + {{ 2071, { 4965, 3009, 2068, 1273 }, 2, {{ 17, 145, 115, 15 }, { 1, 146, 116, 15 }}}, + { 1631, { 3909, 2369, 1628, 1002 }, 2, {{ 14, 74, 46, 14 }, { 21, 75, 47, 14 }}}, + { 1171, { 2805, 1700, 1168, 719 }, 2, {{ 29, 54, 24, 15 }, { 19, 55, 25, 15 }}}, + { 901, { 2157, 1307, 898, 553 }, 2, {{ 11, 45, 15, 15 }, { 46, 46, 16, 15 }}}}, + 6, { 6, 30, 58, 86, 114, 142 }}, + { 34, 153, 2761, 3, { 14, 13, 16, 12 }, + {{ 2191, { 5253, 3183, 2188, 1347 }, 2, {{ 13, 145, 115, 15 }, { 6, 146, 116, 15 }}}, + { 1725, { 4134, 2506, 1722, 1060 }, 2, {{ 14, 74, 46, 14 }, { 23, 75, 47, 14 }}}, + { 1231, { 2949, 1787, 1228, 756 }, 2, {{ 44, 54, 24, 15 }, { 7, 55, 25, 15 }}}, + { 961, { 2301, 1394, 958, 590 }, 2, {{ 59, 46, 16, 15 }, { 1, 47, 17, 15 }}}}, + 6, { 6, 34, 62, 90, 118, 146 }}, + { 35, 157, 2876, 0, { 14, 13, 16, 12 }, + {{ 2306, { 5529, 3351, 2303, 1417 }, 2, {{ 12, 151, 121, 15 }, { 7, 152, 122, 15 }}}, + { 1812, { 4343, 2632, 1809, 1113 }, 2, {{ 12, 75, 47, 14 }, { 26, 76, 48, 14 }}}, + { 1286, { 3081, 1867, 1283, 790 }, 2, {{ 39, 54, 24, 15 }, { 14, 55, 25, 15 }}}, + { 986, { 2361, 1431, 983, 605 }, 2, {{ 22, 45, 15, 15 }, { 41, 46, 16, 15 }}}}, + 7, { 6, 30, 54, 78, 102, 126, 150 }}, + { 36, 161, 3034, 0, { 14, 13, 16, 12 }, + {{ 2434, { 5836, 3537, 2431, 1496 }, 2, {{ 6, 151, 121, 15 }, { 14, 152, 122, 15 }}}, + { 1914, { 4588, 2780, 1911, 1176 }, 2, {{ 6, 75, 47, 14 }, { 34, 76, 48, 14 }}}, + { 1354, { 3244, 1966, 1351, 832 }, 2, {{ 46, 54, 24, 15 }, { 10, 55, 25, 15 }}}, + { 1054, { 2524, 1530, 1051, 647 }, 2, {{ 2, 45, 15, 15 }, { 64, 46, 16, 15 }}}}, + 7, { 6, 24, 50, 76, 102, 128, 154 }}, + { 37, 165, 3196, 0, { 14, 13, 16, 12 }, + {{ 2566, { 6153, 3729, 2563, 1577 }, 2, {{ 17, 152, 122, 15 }, { 4, 153, 123, 15 }}}, + { 1992, { 4775, 2894, 1989, 1224 }, 2, {{ 29, 74, 46, 14 }, { 14, 75, 47, 14 }}}, + { 1426, { 3417, 2071, 1423, 876 }, 2, {{ 49, 54, 24, 15 }, { 10, 55, 25, 15 }}}, + { 1096, { 2625, 1591, 1093, 673 }, 2, {{ 24, 45, 15, 15 }, { 46, 46, 16, 15 }}}}, + 7, { 6, 28, 54, 80, 106, 132, 158 }}, + { 38, 169, 3362, 0, { 14, 13, 16, 12 }, + {{ 2702, { 6479, 3927, 2699, 1661 }, 2, {{ 4, 152, 122, 15 }, { 18, 153, 123, 15 }}}, + { 2102, { 5039, 3054, 2099, 1292 }, 2, {{ 13, 74, 46, 14 }, { 32, 75, 47, 14 }}}, + { 1502, { 3599, 2181, 1499, 923 }, 2, {{ 48, 54, 24, 15 }, { 14, 55, 25, 15 }}}, + { 1142, { 2735, 1658, 1139, 701 }, 2, {{ 42, 45, 15, 15 }, { 32, 46, 16, 15 }}}}, + 7, { 6, 32, 58, 84, 110, 136, 162 }}, + { 39, 173, 3532, 0, { 14, 13, 16, 12 }, + {{ 2812, { 6743, 4087, 2809, 1729 }, 2, {{ 20, 147, 117, 15 }, { 4, 148, 118, 15 }}}, + { 2216, { 5313, 3220, 2213, 1362 }, 2, {{ 40, 75, 47, 14 }, { 7, 76, 48, 14 }}}, + { 1582, { 3791, 2298, 1579, 972 }, 2, {{ 43, 54, 24, 15 }, { 22, 55, 25, 15 }}}, + { 1222, { 2927, 1774, 1219, 750 }, 2, {{ 10, 45, 15, 15 }, { 67, 46, 16, 15 }}}}, + 7, { 6, 26, 54, 82, 110, 138, 166 }}, + { 40, 177, 3706, 0, { 14, 13, 16, 12 }, + {{ 2956, { 7089, 4296, 2953, 1817 }, 2, {{ 19, 148, 118, 15 }, { 6, 149, 119, 15 }}}, + { 2334, { 5596, 3391, 2331, 1435 }, 2, {{ 18, 75, 47, 14 }, { 31, 76, 48, 14 }}}, + { 1666, { 3993, 2420, 1663, 1024 }, 2, {{ 34, 54, 24, 15 }, { 34, 55, 25, 15 }}}, + { 1276, { 3057, 1852, 1273, 784 }, 2, {{ 20, 45, 15, 15 }, { 61, 46, 16, 15 }}}}, + 7, { 6, 30, 58, 86, 114, 142, 170 }} +}; + +/* + * αのべき表現→多項式係数の整数表現 + */ +static const unsigned char qr_exp2fac[256] = { + 1, 2, 4, 8, 16, 32, 64,128, 29, 58,116,232,205,135, 19, 38, + 76,152, 45, 90,180,117,234,201,143, 3, 6, 12, 24, 48, 96,192, + 157, 39, 78,156, 37, 74,148, 53,106,212,181,119,238,193,159, 35, + 70,140, 5, 10, 20, 40, 80,160, 93,186,105,210,185,111,222,161, + 95,190, 97,194,153, 47, 94,188,101,202,137, 15, 30, 60,120,240, + 253,231,211,187,107,214,177,127,254,225,223,163, 91,182,113,226, + 217,175, 67,134, 17, 34, 68,136, 13, 26, 52,104,208,189,103,206, + 129, 31, 62,124,248,237,199,147, 59,118,236,197,151, 51,102,204, + 133, 23, 46, 92,184,109,218,169, 79,158, 33, 66,132, 21, 42, 84, + 168, 77,154, 41, 82,164, 85,170, 73,146, 57,114,228,213,183,115, + 230,209,191, 99,198,145, 63,126,252,229,215,179,123,246,241,255, + 227,219,171, 75,150, 49, 98,196,149, 55,110,220,165, 87,174, 65, + 130, 25, 50,100,200,141, 7, 14, 28, 56,112,224,221,167, 83,166, + 81,162, 89,178,121,242,249,239,195,155, 43, 86,172, 69,138, 9, + 18, 36, 72,144, 61,122,244,245,247,243,251,235,203,139, 11, 22, + 44, 88,176,125,250,233,207,131, 27, 54,108,216,173, 71,142, 1 +}; + +/* + * 多項式係数の整数表現→αのべき表現 + */ +static const unsigned char qr_fac2exp[256] = { + NAV, 0, 1, 25, 2, 50, 26,198, 3,223, 51,238, 27,104,199, 75, + 4,100,224, 14, 52,141,239,129, 28,193,105,248,200, 8, 76,113, + 5,138,101, 47,225, 36, 15, 33, 53,147,142,218,240, 18,130, 69, + 29,181,194,125,106, 39,249,185,201,154, 9,120, 77,228,114,166, + 6,191,139, 98,102,221, 48,253,226,152, 37,179, 16,145, 34,136, + 54,208,148,206,143,150,219,189,241,210, 19, 92,131, 56, 70, 64, + 30, 66,182,163,195, 72,126,110,107, 58, 40, 84,250,133,186, 61, + 202, 94,155,159, 10, 21,121, 43, 78,212,229,172,115,243,167, 87, + 7,112,192,247,140,128, 99, 13,103, 74,222,237, 49,197,254, 24, + 227,165,153,119, 38,184,180,124, 17, 68,146,217, 35, 32,137, 46, + 55, 63,209, 91,149,188,207,205,144,135,151,178,220,252,190, 97, + 242, 86,211,171, 20, 42, 93,158,132, 60, 57, 83, 71,109, 65,162, + 31, 45, 67,216,183,123,164,118,196, 23, 73,236,127, 12,111,246, + 108,161, 59, 82, 41,157, 85,170,251, 96,134,177,187,204, 62, 90, + 203, 89, 95,176,156,169,160, 81, 11,245, 22,235,122,117, 44,215, + 79,174,213,233,230,231,173,232,116,214,244,234,168, 80, 88,175 +}; + +/* + * 誤り訂正生成多項式の第2項以降の係数表(べき表現) + */ +static const unsigned char qr_gftable[QR_RSW_MAX+1][QR_RSW_MAX] = { + {0},{0},{0},{0},{0},{0},{0},{87,229,146,149,238,102,21}, + {0},{0},{251,67,46,61,118,70,64,94,32,45}, + {0},{0},{74,152,176,100,86,100,106,104,130,218,206,140,78}, + {0},{8,183,61,91,202,37,51,58,58,237,140,124,5,99,105}, + {120,104,107,109,102,161,76,3,91,191,147,169,182,194,225,120}, + {43,139,206,78,43,239,123,206,214,147,24,99,150,39,243,163,136}, + {215,234,158,94,184,97,118,170,79,187,152,148,252,179,5,98,96,153}, + {0},{17,60,79,50,61,163,26,187,202,180,221,225,83,239,156,164,212,212,188,190}, + {0},{210,171,247,242,93,230,14,109,221,53,200,74,8,172,98,80,219,134,160,105,165,231}, + {0},{229,121,135,48,211,117,251,126,159,180,169,152,192,226,228,218,111,0,117,232,87,96,227,21}, + {0},{173,125,158,2,103,182,118,17,145,201,111,28,165,53,161,21,245,142,13,102,48,227,153,145,218,70}, + {0},{168,223,200,104,224,234,108,180,110,190,195,147,205,27,232,201,21,43,245,87,42,195,212,119,242,37,9,123}, + {0},{41,173,145,152,216,31,179,182,50,48,110,86,239,96,222,125,42,173,226,193,224,130,156,37,251,216,238,40,192,180}, + {0},{10,6,106,190,249,167,4,67,209,138,138,32,242,123,89,27,120,185,80,156,38,69,171,60,28,222,80,52,254,185,220,241}, + {0},{111,77,146,94,26,21,108,19,105,94,113,193,86,140,163,125,58,158,229,239,218,103,56,70,114,61,183,129,167,13,98,62,129,51}, + {0},{200,183,98,16,172,31,246,234,60,152,115,0,167,152,113,248,238,107,18,63,218,37,87,210,105,177,120,74,121,196,117,251,113,233,30,120}, + {0},{159,34,38,228,230,59,243,95,49,218,176,164,20,65,45,111,39,81,49,118,113,222,193,250,242,168,217,41,164,247,177,30,238,18,120,153,60,193}, + {0},{59,116,79,161,252,98,128,205,128,161,247,57,163,56,235,106,53,26,187,174,226,104,170,7,175,35,181,114,88,41,47,163,125,134,72,20,232,53,35,15}, + {0},{250,103,221,230,25,18,137,231,0,3,58,242,221,191,110,84,230,8,188,106,96,147,15,131,139,34,101,223,39,101,213,199,237,254,201,123,171,162,194,117,50,96}, + {0},{190,7,61,121,71,246,69,55,168,188,89,243,191,25,72,123,9,145,14,247,1,238,44,78,143,62,224,126,118,114,68,163,52,194,217,147,204,169,37,130,113,102,73,181}, + {0},{112,94,88,112,253,224,202,115,187,99,89,5,54,113,129,44,58,16,135,216,169,211,36,1,4,96,60,241,73,104,234,8,249,245,119,174,52,25,157,224,43,202,223,19,82,15}, + {0},{228,25,196,130,211,146,60,24,251,90,39,102,240,61,178,63,46,123,115,18,221,111,135,160,182,205,107,206,95,150,120,184,91,21,247,156,140,238,191,11,94,227,84,50,163,39,34,108}, + {0},{232,125,157,161,164,9,118,46,209,99,203,193,35,3,209,111,195,242,203,225,46,13,32,160,126,209,130,160,242,215,242,75,77,42,189,32,113,65,124,69,228,114,235,175,124,170,215,232,133,205}, + {0},{116,50,86,186,50,220,251,89,192,46,86,127,124,19,184,233,151,215,22,14,59,145,37,242,203,134,254,89,190,94,59,65,124,113,100,233,235,121,22,76,86,97,39,242,200,220,101,33,239,254,116,51}, + {0},{183,26,201,87,210,221,113,21,46,65,45,50,238,184,249,225,102,58,209,218,109,165,26,95,184,192,52,245,35,254,238,175,172,79,123,25,122,43,120,108,215,80,128,201,235,8,153,59,101,31,198,76,31,156}, + {0},{106,120,107,157,164,216,112,116,2,91,248,163,36,201,202,229,6,144,254,155,135,208,170,209,12,139,127,142,182,249,177,174,190,28,10,85,239,184,101,124,152,206,96,23,163,61,27,196,247,151,154,202,207,20,61,10}, + {0},{82,116,26,247,66,27,62,107,252,182,200,185,235,55,251,242,210,144,154,237,176,141,192,248,152,249,206,85,253,142,65,165,125,23,24,30,122,240,214,6,129,218,29,145,127,134,206,245,117,29,41,63,159,142,233,125,148,123}, + {0},{107,140,26,12,9,141,243,197,226,197,219,45,211,101,219,120,28,181,127,6,100,247,2,205,198,57,115,219,101,109,160,82,37,38,238,49,160,209,121,86,11,124,30,181,84,25,194,87,65,102,190,220,70,27,209,16,89,7,33,240}, + {0},{65,202,113,98,71,223,248,118,214,94,0,122,37,23,2,228,58,121,7,105,135,78,243,118,70,76,223,89,72,50,70,111,194,17,212,126,181,35,221,117,235,11,229,149,147,123,213,40,115,6,200,100,26,246,182,218,127,215,36,186,110,106}, + {0},{45,51,175,9,7,158,159,49,68,119,92,123,177,204,187,254,200,78,141,149,119,26,127,53,160,93,199,212,29,24,145,156,208,150,218,209,4,216,91,47,184,146,47,140,195,195,125,242,238,63,99,108,140,230,242,31,204,11,178,243,217,156,213,231}, + {0},{5,118,222,180,136,136,162,51,46,117,13,215,81,17,139,247,197,171,95,173,65,137,178,68,111,95,101,41,72,214,169,197,95,7,44,154,77,111,236,40,121,143,63,87,80,253,240,126,217,77,34,232,106,50,168,82,76,146,67,106,171,25,132,93,45,105}, + {0},{247,159,223,33,224,93,77,70,90,160,32,254,43,150,84,101,190,205,133,52,60,202,165,220,203,151,93,84,15,84,253,173,160,89,227,52,199,97,95,231,52,177,41,125,137,241,166,225,118,2,54,32,82,215,175,198,43,238,235,27,101,184,127,3,5,8,163,238} +}; + +#define F0 QR_MM_FUNC +#define F1 (QR_MM_FUNC | QR_MM_BLACK) + +/* + * 位置検出パターンのデータ + */ +static const qr_byte_t qr_finderpattern[QR_DIM_FINDER][QR_DIM_FINDER] = { + { F1, F1, F1, F1, F1, F1, F1 }, + { F1, F0, F0, F0, F0, F0, F1 }, + { F1, F0, F1, F1, F1, F0, F1 }, + { F1, F0, F1, F1, F1, F0, F1 }, + { F1, F0, F1, F1, F1, F0, F1 }, + { F1, F0, F0, F0, F0, F0, F1 }, + { F1, F1, F1, F1, F1, F1, F1 } +}; + +/* + * 位置合わせパターンのデータ + */ +static const qr_byte_t qr_alignpattern[QR_DIM_ALIGN][QR_DIM_ALIGN] = { + { F1, F1, F1, F1, F1 }, + { F1, F0, F0, F0, F1 }, + { F1, F0, F1, F0, F1 }, + { F1, F0, F0, F0, F1 }, + { F1, F1, F1, F1, F1 } +}; + +#undef F0 +#undef F1 + +/* + * モード指示子(英字, 英数字, 8ビットバイト, 漢字) + */ +static const int qr_modeid[QR_EM_COUNT] = { 0x01, 0x02, 0x04, 0x08 }; + +/* + * 符号化モード名 (不使用) + */ +/*const char *qr_modename[QR_EM_COUNT] = { + "Numeric", "Alnum", "8bit-byte", "Kanji" +};*/ + +/* + * エラー訂正レベル名 + */ +const char *qr_eclname[QR_ECL_COUNT] = { "L", "M", "Q", "H" }; + +/* + * 英数字モードの符号化表 + */ +static const signed char qr_alnumtable[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, + -1, 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, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +/* + * 形式情報(2箇所)の座標(下位ビットから) + * (負数は下端/右端からのオフセット) + */ +static const qr_coord_t qr_fmtinfopos[2][QR_FIN_MAX] = { + {{ 0, 8 }, { 1, 8 }, { 2, 8 }, { 3, 8 }, + { 4, 8 }, { 5, 8 }, { 7, 8 }, { 8, 8 }, + { -7, 8 }, { -6, 8 }, { -5, 8 }, { -4, 8 }, + { -3, 8 }, { -2, 8 }, { -1, 8 }}, + {{ 8, -1 }, { 8, -2 }, { 8, -3 }, { 8, -4 }, + { 8, -5 }, { 8, -6 }, { 8, -7 }, { 8, -8 }, + { 8, 7 }, { 8, 5 }, { 8, 4 }, { 8, 3 }, + { 8, 2 }, { 8, 1 }, { 8, 0 }} +}; + +/* + * 形式情報の固定黒モジュール + */ +static const qr_coord_t qr_fmtblackpos = { -8, 8 }; + +/* + * 型番情報(2箇所)の座標(下位ビットから) + * (負数は下端/右端からのオフセット) + */ +static const qr_coord_t qr_verinfopos[2][QR_VIN_MAX] = { + {{ -11, 0 }, { -10, 0 }, { -9, 0 }, + { -11, 1 }, { -10, 1 }, { -9, 1 }, + { -11, 2 }, { -10, 2 }, { -9, 2 }, + { -11, 3 }, { -10, 3 }, { -9, 3 }, + { -11, 4 }, { -10, 4 }, { -9, 4 }, + { -11, 5 }, { -10, 5 }, { -9, 5 }}, + {{ 0, -11 }, { 0, -10 }, { 0, -9 }, + { 1, -11 }, { 1, -10 }, { 1, -9 }, + { 2, -11 }, { 2, -10 }, { 2, -9 }, + { 3, -11 }, { 3, -10 }, { 3, -9 }, + { 4, -11 }, { 4, -10 }, { 4, -9 }, + { 5, -11 }, { 5, -10 }, { 5, -9 }} +}; + +/* + * 型番情報(型番7〜40について有効) + */ +static const long qr_verinfo[QR_VER_MAX+1] = { + -1L, -1L, -1L, -1L, -1L, -1L, + -1L, 0x07c94L, 0x085bcL, 0x09a99L, 0x0a4d3L, 0x0bbf6L, + 0x0c762L, 0x0d847L, 0x0e60dL, 0x0f928L, 0x10b78L, 0x1145dL, + 0x12a17L, 0x13532L, 0x149a6L, 0x15683L, 0x168c9L, 0x177ecL, + 0x18ec4L, 0x191e1L, 0x1afabL, 0x1b08eL, 0x1cc1aL, 0x1d33fL, + 0x1ed75L, 0x1f250L, 0x209d5L, 0x216f0L, 0x228baL, 0x2379fL, + 0x24b0bL, 0x2542eL, 0x26a64L, 0x27541L, 0x28c69L +}; + +/* + * 一連の処理をする関数ポインタ型 + */ +typedef int (*qr_funcs)(QRCode *); + +/* + * 内部処理用関数のプロトタイプ + */ +static void qrAddDataBits(QRCode *qr, int n, int word); +static int qrInitDataWord(QRCode *qr); +static int qrEncodeDataWord(QRCode *qr, const qr_byte_t *source, int size, int mode); +static int qrFinalizeDataWord(QRCode *qr); +static int qrComputeECWord(QRCode *qr); +static int qrMakeCodeWord(QRCode *qr); +static int qrFillFunctionPattern(QRCode *qr); +static int qrFillCodeWord(QRCode *qr); +static void qrInitPosition(QRCode *qr); +static void qrNextPosition(QRCode *qr); +static int qrSelectMaskPattern(QRCode *qr); +static int qrApplyMaskPattern(QRCode *qr); +static int qrApplyMaskPattern2(QRCode *qr, int type); +static long qrEvaluateMaskPattern(QRCode *qr); +static int qrFillFormatInfo(QRCode *qr); + + +#endif /* _QR_PRIVATE_H_ */ diff --git a/kvm_system/main/lib/libqr/qr_util.h b/kvm_system/main/lib/libqr/qr_util.h new file mode 100644 index 0000000..51cca39 --- /dev/null +++ b/kvm_system/main/lib/libqr/qr_util.h @@ -0,0 +1,87 @@ +/* + * QR Code Generator Library: Header for Utility + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#ifndef _QR_UTIL_H_ +#define _QR_UTIL_H_ + +#include "qr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * Determine the module is a dark module or not. + */ +#define qrIsBlack(qr, i, j) (((qr)->symbol[(i)][(j)] & QR_MM_BLACK) != 0) + +/* + * Deallocate and set to NULL. + */ +#define qrFree(ptr) { if ((ptr) != NULL) { free(ptr); (ptr) = NULL; } } + +/* + * Current function name macro. + */ +QR_API extern const char *(*qrGetCurrentFunctionName)(void); +#if defined(__FUNCTION__) +#define _QR_FUNCTION ((qrGetCurrentFunctionName) ? qrGetCurrentFunctionName() : __FUNCTION__) +#elif defined(__func__) +#define _QR_FUNCTION ((qrGetCurrentFunctionName) ? qrGetCurrentFunctionName() : __func__) +#else +#define _QR_FUNCTION ((qrGetCurrentFunctionName) ? qrGetCurrentFunctionName() : "?") +#endif + +/* + * Maximum length of filename extensions. + */ +#define QR_EXT_MAX_LEN 4 + +/* + * Constatns. + */ +extern QR_API const qr_vertable_t qr_vertable[]; +/*extern QR_API const char *qr_modename[]; */ +extern QR_API const char *qr_eclname[]; + +/* + * Functions for utility. + */ +QR_API const char *qrVersion(void); +QR_API const char *qrMimeType(int format); +QR_API const char *qrExtension(int format); +QR_API const char *qrStrError(int errcode); +QR_API void qrSetErrorInfo(QRCode *qr, int errnum, const char *param); +QR_API void qrSetErrorInfo2(QRCode *qr, int errnum, const char *param); +QR_API void qrSetErrorInfo3(QRCode *qr, int errnum, const char *fmt, ...); +QR_API int qrGetEncodedLength(QRCode *qr, int size); +QR_API int qrGetEncodedLength2(QRCode *qr, int size, int mode); +QR_API int qrGetEncodableLength(QRCode *qr, int size); +QR_API int qrGetEncodableLength2(QRCode *qr, int size, int mode); +QR_API int qrRemainedDataBits(QRCode *qr); + +/* + * Functions for checking datatype. + */ +QR_API int qrDetectDataType(const qr_byte_t *source, int size); +QR_API int qrStrPosNotNumeric(const qr_byte_t *source, int size); +QR_API int qrStrPosNotAlnum(const qr_byte_t *source, int size); +QR_API int qrStrPosNotKanji(const qr_byte_t *source, int size); +QR_API int qrStrPosNot8bit(const qr_byte_t *source, int size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _QR_UTIL_H_ */ diff --git a/kvm_system/main/lib/libqr/qrcmd.c b/kvm_system/main/lib/libqr/qrcmd.c new file mode 100644 index 0000000..c090c11 --- /dev/null +++ b/kvm_system/main/lib/libqr/qrcmd.c @@ -0,0 +1,626 @@ +/* + * QR Code Generator Library: Command Line Interface + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#include "qrcmd.h" + +#ifdef WIN32 +#define err(code, ...) { \ + printf(__VA_ARGS__); \ + printf(": %s\r\n", strerror(errno)); \ + exit(code); \ +} +#define errx(code, ...) { \ + printf(__VA_ARGS__); \ + printf("\r\n"); \ + exit(code); \ +} +#else +#include +#endif + +#include +#include +#include + +#ifdef WIN32 +#include +#define writeln(msg) printf(msg "\r\n") +#define ewriteln(msg) printf(msg "\r\n") +#define writelnf(fmt, ...) printf(fmt "r\n", __VA_ARGS__) +#define ewritelnf(fmt, ...) printf(fmt "\r\n", __VA_ARGS__) +#else +#define writeln(msg) printf(msg "\n") +#define ewriteln(msg) fprintf(stderr, msg "\n") +#define writelnf(fmt, ...) printf(fmt "\n", __VA_ARGS__) +#define ewritelnf(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__) +#endif + +/* {{{ main() */ + +int +main(int argc, char **argv) +{ + QRCMD_PTR_TYPE *qr; + int mag = 1; + int sep = QR_DIM_SEP; + int fmt = QR_FMT_PBM; + QRCMD_EXTRA_PARAM_A + char output[PATH_MAX] = { '\0' }; + char *ptr = &(output[0]); + int result = 0; + +#ifdef WIN32 + setmode(fileno(stdin), O_BINARY); + setmode(fileno(stdout), O_BINARY); +#endif + + if (argc <= 1) { + writelnf("%s: QR Code Generator", argv[0]); + writelnf("try '%s --help' for more information", argv[0]); + return 1; + } + + /* + * QRコードオブジェクトを生成し、コマンド行引数からパラメータと入力データを設定する + */ + qr = qrGetParameter(argc, argv, &fmt, &sep, &mag, QRCMD_EXTRA_PARAM_C &ptr); + + /* + * データコード語をエンコードし、シンボルを配置する + */ + if (!qrCmdFinalize(qr)) { + ewritelnf("%s: %s", argv[0], qrCmdGetErrorInfo(qr)); + qrCmdDestroy(qr); + return 1; + } + + /* + * シンボルを出力する + */ +#ifdef QRCMD_STRUCTURED_APPEND + if (extra == -1) { + /* + * 連番出力 + */ + int i; + for (i = 0; i < qr->num; i++) { + if (output[0] == '\0') { + result = qrOutputSymbol(qr->qrs[i], stdout, fmt, sep, mag); + } else { + char outputs[PATH_MAX] = { '\0' }; + int written = 0; + written = snprintf(&(outputs[0]), PATH_MAX, "%s%02d.%s", + &(output[0]), i + 1, qrExtension(fmt)); + if (written < 0 || written >= PATH_MAX) { + ewritelnf("%s: output pathname is too long", argv[0]); + qrCmdDestroy(qr); + return 1; + } + result = qrOutputSymbol2(qr->qrs[i], &(outputs[0]), fmt, sep, mag); + } + if (result == -1) { + ewritelnf("%s: QR Code #%d: %s", argv[0], i, qrCmdGetErrorInfo(qr)); + qrCmdDestroy(qr); + return 1; + } + } + } else +#endif + if (output[0] == '\0') { + result = qrCmdOutputSymbol(qr, stdout, fmt, sep, mag QRCMD_EXTRA_PARAM_B); + } else { + result = qrCmdOutputSymbol2(qr, &(output[0]), fmt, sep, mag QRCMD_EXTRA_PARAM_B); + } + if (result == -1) { + ewritelnf("%s: %s", argv[0], qrCmdGetErrorInfo(qr)); + qrCmdDestroy(qr); + return 1; + } + + qrCmdDestroy(qr); + return 0; +} + +/* }}} main() */ +/* {{{ qrShowHelp() */ + +/* + * 使用方法を表示する + */ +void +qrShowHelp(void) +{ + writeln("QR Code Generator"); + writeln(); + writeln("QR Code (R) is registered trademarks of DENSO WAVE INCORPORATED"); + writeln("in JAPAN and other countries."); + writeln(); + writelnf("usage: %s [options ...] [ [ [mode] [text | -i file] ] ...] ", QRCMD_PROG_NAME); + writeln(); + writeln("input:"); + writeln(" The data which is stored in QR Code Symbol (at least 1 character)."); + writeln(" If it is not specified, read from stdin."); + writeln(); + writeln("examples:"); + writelnf(" %s -eM -x6 < input.txt > output.pbm", QRCMD_PROG_NAME); + writelnf(" %s -eM -x6 -o output.pbm -i input.txt", QRCMD_PROG_NAME); + writelnf(" %s -v2 -fBMP -o foobar.bmp -mA 'FOOBAR'", QRCMD_PROG_NAME); + writelnf(" %s -x3 -fSVG -o mixed.svg -mA 'ALNUM' -m8 ' ' -mN '001'", QRCMD_PROG_NAME); + writelnf(" %s -fAA 'Ascii Art'", QRCMD_PROG_NAME); + writeln(); + writeln("options:"); + writeln(" -V show program's version number and exit"); + writeln(" -?, -h, --help show this help message and exit"); +#ifdef QRCMD_STRUCTURED_APPEND + writeln(" -v, --version=NUM symbol version (1-40, default: 1)"); +#else + writeln(" -v, --version=NUM symbol version (1-40, default: auto)"); +#endif + writeln(" -m, --mode=MODE encoding mode (N,A,8,K,S, default: S)"); + writeln(" N: numeric, A: uppercase alphabet and numeric,"); + writeln(" 8: 8-bit byte, K: JIS X 0208 Kanji, S: auto"); + writeln(" -e, --eclevel=LEVEL error correction level (L,M,Q,H, default: M)"); + writeln(" L: 7%%, M: 15%%, Q: 25%%, H: 30%%"); + writeln(" -p, --pattern=NUM mask pattern (0-7, default: auto)"); + writeln(" -x, --magnify=NUM magnifying ratio (1-16, default: 1)"); + writeln(" -s, --separator=NUM separator pattan width (0-16, default: 4)"); + writeln(" '4' is the lower limit of the QR Code specification."); + writeln(" -f, --format=FORMAT output format (default: PBM)"); + writeln(" Available formats are followings."); + writeln(" PNG, BMP, TIFF, PBM, SVG, JSON, DIGIT, ASCII"); + writeln(" These are case-insensitive and some have aliases."); + writeln(" DIGIT -> 01"); + writeln(" ASCII -> asciiart, aa"); + writeln(" JSON -> javascript, js"); + writeln(" TIFF -> tif"); +#ifdef QRCMD_STRUCTURED_APPEND + writelnf(" -a, --maxnum=NUM maximum number of symbols (1-%d, default: %d)", QR_STA_MAX, QR_STA_MAX); + writeln(" -z, --order=NUM ordering method of symbols, in case NUM is ..."); + writeln(" = 0 (default): order to square as possible"); + writeln(" >= 1: order each NUM symbols to horizontal"); + writeln(" <= -1: order each NUM symbols to vertical"); + writeln(" --serial output as serial numbered images"); + writeln(" Number and extension will be added to output pathname."); + writeln(" (default: output as a combined image)"); +#endif + writeln(" -o, --output=PATH output pathname (default: write to stdout)"); + writeln(" -i, --input=PATH input pathname (default: read from stdin)"); + writeln(" To specify multiple files, set this option"); + writeln(" before every filename. And the encoding mode"); + writeln(" can be specified to each files."); + writeln(" (e.g. -mA -i file1 -mK -i file2 ...)"); +} + +/* }}} qrShowHelp() */ +/* {{{ utilities for qrGetParameter() */ + +#define QR_SHORT_OPT(name) (d = 2, !strncmp(ptr, name, d)) +#define QR_LONG_OPT(name) (d = sizeof(name), !strncmp(ptr, name "=", d)) + +#define QR_GETOPT_NEXT() { \ + opt = ptr; \ + ptr += d; \ + if (d == 2 && *ptr == '\0') { \ + i++; \ + if (i == argc) { \ + errx(1, "%s: %s", opt, qrStrError(QR_ERR_EMPTY_PARAM)); \ + } \ + ptr = argv[i]; \ + } \ + if (ptr == NULL || *ptr == '\0') { \ + errx(1, "%s: %s", opt, qrStrError(QR_ERR_EMPTY_PARAM)); \ + } \ +} + +/* }}} utilities for qrGetParameter() */ +/* {{{ qrGetParameter() */ + +/* + * コマンド行引数からパラメータと入力データを設定し、出力ファイル名を取得する + */ +QRCMD_PTR_TYPE * +qrGetParameter(int argc, char **argv, + int *fmt, int *sep, int *mag, QRCMD_EXTRA_PARAM_D char **output) +{ + QRCMD_PTR_TYPE *qr; + char *opt, *ptr; + int i; + size_t d; + int version = QRCMD_DEFAULT_VERSION; + int mode = QR_EM_AUTO; + int eclevel = QR_ECL_M; + int masktype = -1; + QRCMD_MAX_NUM_A + int errcode = QR_ERR_NONE; + int has_data = 0; + + /* + * 引数のパラメータを取得する + */ + for (i = 1; i < argc; i++) { + ptr = argv[i]; + if (!strcmp(ptr, "-?") || !strcasecmp(ptr, "-h") || !strcmp(ptr, "--help")) { + /* + * 使用方法 + */ + qrShowHelp(); + exit(1); + + } else if (!strcmp(ptr, "-V")) { + /* + * バージョン + */ + writelnf("%s: QR Code Generator", QRCMD_PROG_NAME); + writelnf("qrcmd version: %s", QRCMD_PROG_VERSION); + writelnf("libqr version: %s", qrVersion()); + exit(1); + + } else if (QR_SHORT_OPT("-v") || QR_LONG_OPT("--version")) { + /* + * 型番 + */ + QR_GETOPT_NEXT(); + if (*ptr < '0' || *ptr > '9') { + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_VERSION)); + } + version = atoi(ptr); + if (version <= 0 || version > QR_VER_MAX) { + errx(1, "%d: %s", version, qrStrError(QR_ERR_INVALID_VERSION)); + } + + } else if (QR_SHORT_OPT("-m") || QR_LONG_OPT("--mode")) { + /* + * 符号化モード + */ + QR_GETOPT_NEXT(); + switch (*ptr) { + case 's': + case 'S': + mode = QR_EM_AUTO; + break; + case 'n': + case 'N': + mode = QR_EM_NUMERIC; + break; + case 'a': + case 'A': + mode = QR_EM_ALNUM; + break; + case '8': + case 'B': + mode = QR_EM_8BIT; + break; + case 'k': + case 'K': + mode = QR_EM_KANJI; + break; + default: + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_MODE)); + } + + } else if (QR_SHORT_OPT("-e") || QR_LONG_OPT("--eclevel")) { + /* + * 誤り訂正レベル + */ + QR_GETOPT_NEXT(); + switch (*ptr) { + case 'l': + case 'L': + eclevel = QR_ECL_L; + break; + case 'm': + case 'M': + eclevel = QR_ECL_M; + break; + case 'q': + case 'Q': + eclevel = QR_ECL_Q; + break; + case 'h': + case 'H': + eclevel = QR_ECL_H; + break; + default: + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_ECL)); + } + + } else if (QR_SHORT_OPT("-p") || QR_LONG_OPT("--pattern")) { + /* + * マスクパターン種別 + */ + QR_GETOPT_NEXT(); + if (*ptr < '0' || *ptr > '9') { + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_MPT)); + } + masktype = atoi(ptr); + if (masktype < 0 || masktype >= QR_MPT_MAX) { + errx(1, "%d: %s", masktype, qrStrError(QR_ERR_INVALID_MPT)); + } + + } else if (QR_SHORT_OPT("-x") || QR_LONG_OPT("--magnify")) { + /* + * ピクセル表示倍率 + */ + QR_GETOPT_NEXT(); + if (*ptr < '0' || *ptr > '9') { + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_MAG)); + } + *mag = atoi(ptr); + if (*mag < 0 || *mag > QR_MAG_MAX) { + errx(1, "%d: %s", *mag, qrStrError(QR_ERR_INVALID_MAG)); + } + + } else if (QR_SHORT_OPT("-s") || QR_LONG_OPT("--separator")) { + /* + * 分離パターン幅 + */ + QR_GETOPT_NEXT(); + if (*ptr < '0' || *ptr > '9') { + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_SEP)); + } + *sep = atoi(ptr); + if (*sep < 0 || *sep > QR_SEP_MAX) { + errx(1, "%d: %s", *sep, qrStrError(QR_ERR_INVALID_SEP)); + } + + } else if (QR_SHORT_OPT("-f") || QR_LONG_OPT("--format")) { + /* + * 出力形式 + */ + QR_GETOPT_NEXT(); + if (!strcasecmp(ptr, "digit") || !strcasecmp(ptr, "01")) { + *fmt = QR_FMT_DIGIT; + } else if (!strcasecmp(ptr, "asciiart") || + !strcasecmp(ptr, "ascii") || + !strcasecmp(ptr, "aa")) + { + *fmt = QR_FMT_ASCII; + } else if (!strcasecmp(ptr, "javascript") || + !strcasecmp(ptr, "json") || + !strcasecmp(ptr, "js")) + { + *fmt = QR_FMT_JSON; + } else if (!strcasecmp(ptr, "pbm")) { + *fmt = QR_FMT_PBM; + } else if (!strcasecmp(ptr, "bmp")) { + *fmt = QR_FMT_BMP; + } else if (!strcasecmp(ptr, "svg")) { + *fmt = QR_FMT_SVG; + } else if (!strcasecmp(ptr, "tiff") | !strcasecmp(ptr, "tif")) { +#ifdef QR_ENABLE_TIFF + *fmt = QR_FMT_TIFF; +#else + +#endif /* QR_ENABLE_TIFF */ +#ifdef QR_ENABLE_PNG + } else if (!strcasecmp(ptr, "png")) { + *fmt = QR_FMT_PNG; +#endif /* QR_ENABLE_PNG */ + } else { + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_FMT)); + } + + } else if (QR_SHORT_OPT("-o") || QR_LONG_OPT("--output")) { + /* + * 出力ファイル名 + */ + QR_GETOPT_NEXT(); + size_t pathlen; + pathlen = strlen(ptr); + if (pathlen >= PATH_MAX) { + errx(1, "argv[%d]: %s", i, strerror(ENAMETOOLONG)); + } + if (*output[0] != '\0') { + errx(1, "%s: %s: Duplicated declaration of output pathname", + *output, ptr); + } + strncpy(*output, ptr, pathlen); + +#ifdef QRCMD_STRUCTURED_APPEND + + } else if (QR_SHORT_OPT("-a") || QR_LONG_OPT("--maxnum")) { + /* + * 最大シンボル数 + */ + QR_GETOPT_NEXT(); + if (*ptr < '0' || *ptr > '9') { + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_MAXNUM)); + } + maxnum = atoi(ptr); + if (maxnum < 2 || maxnum > QR_STA_MAX) { + errx(1, "%d: %s", maxnum, qrStrError(QR_ERR_INVALID_MAXNUM)); + } + + } else if (QR_SHORT_OPT("-z") || QR_LONG_OPT("--order")) { + /* + * 並べ方 + */ + QR_GETOPT_NEXT(); + if (*ptr != '-' && (*ptr < '0' || *ptr > '9')) { + /*errx(1, "%s: %s", ptr, qrStrError(QR_ERR_UNKNOWN));*/ + continue; + } + *order = atoi(ptr); + + } else if (!strcmp(ptr, "--serial")) { + /* + * 連番 + */ + if (*extra != 0) { + errx(1, "Serial image output and GIF animation output are exclusive."); + } + *extra = -1; + + } else if (!strcmp(ptr, "--animation") || !strncmp(ptr, "--animation=", 12)) { + /* + * GIFアニメ + */ + if (*extra != 0) { + errx(1, "Serial image output and GIF animation output are exclusive."); + } + if (strlen(ptr) == 11) { + *extra = 100; + } else { + double _f; + ptr += 12; + if (*ptr < '0' || *ptr > '9') { + errx(1, "%s: Invalid animation delay.", ptr); + } + _f = atof(ptr); + *extra = (int)(_f * 100); + if (*extra == 0) { + errx(1, "Zero animation delay."); + } + } + +#endif /* QRCMD_STRUCTURED_APPEND */ + + } else { + break; + } + } + + /* + * QRコードオブジェクトを初期化する + */ + qr = qrCmdInit(version, mode, eclevel, masktype, QRCMD_MAX_NUM_B &errcode); + if (qr == NULL) { + errx(1, "%s", qrStrError(errcode)); + } + + /* + * 入力データを取得する + */ + for (; i < argc; i++) { + ptr = argv[i]; + if (QR_SHORT_OPT("-m") || QR_LONG_OPT("--mode")) { + /* + * 符号化モードを上書き + */ + QR_GETOPT_NEXT(); + switch (*ptr) { + case 'n': + case 'N': + mode = QR_EM_NUMERIC; + break; + case 'a': + case 'A': + mode = QR_EM_ALNUM; + break; + case '8': + case 'B': + mode = QR_EM_8BIT; + break; + case 'k': + case 'K': + mode = QR_EM_KANJI; + break; + default: + qrCmdDestroy(qr); + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_MODE)); + } + + } else if (QR_SHORT_OPT("-i") || QR_LONG_OPT("--input")) { + qr_byte_t source[QRCMD_SRC_MAX]; + int srclen = 0; + FILE *fp; + int c; + /* + * 入力データをファイルから読む + */ + QR_GETOPT_NEXT(); + fp = fopen(ptr, "rb"); + if (fp == NULL) { + qrCmdDestroy(qr); + err(1, "%s", ptr); + } + while ((c = fgetc(fp)) != EOF && srclen < QRCMD_SRC_MAX){ + source[srclen++] = (qr_byte_t)c; + } + fclose(fp); + if (srclen == 0) { + qrCmdDestroy(qr); + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_EMPTY_SRC)); + } else if (c != EOF) { + qrCmdDestroy(qr); + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_LARGE_SRC)); + } + if (!qrCmdAddData2(qr, source, srclen, mode)) { + char errinfo[QR_ERR_MAX]; + snprintf(&(errinfo[0]), QR_ERR_MAX, "%s: %s", ptr, qrCmdGetErrorInfo(qr)); + qrCmdDestroy(qr); + errx(1, "%s", errinfo); + } + has_data++; + + } else if (*ptr != '-') { + qr_byte_t source[QRCMD_SRC_MAX]; + int srclen = 0; + /* + * 入力データを引数から得る + */ + srclen = strlen(ptr); + if (srclen == 0) { + qrCmdDestroy(qr); + errx(1, "argv[%d]: %s", i, qrStrError(QR_ERR_EMPTY_SRC)); + } else if (srclen > QRCMD_SRC_MAX) { + qrCmdDestroy(qr); + errx(1, "argv[%d]: %s", i, qrStrError(QR_ERR_LARGE_SRC)); + } + memcpy(&(source[0]), ptr, (size_t)srclen); + if (!qrCmdAddData2(qr, source, srclen, mode)) { + char errinfo[QR_ERR_MAX]; + snprintf(&(errinfo[0]), QR_ERR_MAX, "argv[%d]: %s", i, qrCmdGetErrorInfo(qr)); + qrCmdDestroy(qr); + errx(1, "%s", errinfo); + } + has_data++; + + } else { + /* + * 未定義オプション + */ + qrCmdDestroy(qr); + errx(1, "%s: %s", ptr, qrStrError(QR_ERR_INVALID_ARG)); + } + } + + /* + * 入力データが空なら、標準入力から読む + */ + if (!has_data) { + qr_byte_t source[QRCMD_SRC_MAX]; + int srclen = 0; + int c; + while ((c = getchar()) != EOF && srclen < QRCMD_SRC_MAX){ + source[srclen++] = (qr_byte_t)c; + } + if (srclen == 0) { + qrCmdDestroy(qr); + errx(1, "-stdin: %s", qrStrError(QR_ERR_EMPTY_SRC)); + } + if (c != EOF) { + qrCmdDestroy(qr); + errx(1, "-stdin: %s", qrStrError(QR_ERR_LARGE_SRC)); + } + if (!qrCmdAddData2(qr, source, srclen, mode)) { + char errinfo[QR_ERR_MAX]; + snprintf(&(errinfo[0]), QR_ERR_MAX, "%s", qrCmdGetErrorInfo(qr)); + qrCmdDestroy(qr); + errx(1, "%s", errinfo); + } + } + + return qr; +} + +/* }}} qrGetParameter() */ diff --git a/kvm_system/main/lib/libqr/qrcmd.h b/kvm_system/main/lib/libqr/qrcmd.h new file mode 100644 index 0000000..4b4f4f2 --- /dev/null +++ b/kvm_system/main/lib/libqr/qrcmd.h @@ -0,0 +1,110 @@ +/* + * QR Code Generator Library: Header for Command Line Interface + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#ifndef _QRCMD_H_ +#define _QRCMD_H_ + +/* {{{ include headers */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "qr.h" +#include "qr_util.h" + +/* }}} */ +/* {{{ version information */ + +#define QRCMD_PROG_VERSION "0.2.0" + +/* }}} */ +/* {{{ whether enable structured append or not */ + +#ifdef QRCMD_STRUCTURED_APPEND +/* {{{ enable structured append */ + +/* parameters */ +#define QRCMD_PTR_TYPE QRStructured +#define QRCMD_EXTRA_PARAM_A int order = 0; int extra = 0; +#define QRCMD_EXTRA_PARAM_B , order +#define QRCMD_EXTRA_PARAM_C &order, &extra, +#define QRCMD_EXTRA_PARAM_D int *order, int *extra, +#define QRCMD_MAX_NUM_A int maxnum = QR_STA_MAX; +#define QRCMD_MAX_NUM_B maxnum, +#define QRCMD_DEFAULT_VERSION 1 + +/* functions */ +#define qrCmdInit qrsInit +#define qrCmdDestroy qrsDestroy +#define qrCmdFinalize qrsFinalize +#define qrCmdGetErrorInfo qrsGetErrorInfo +#define qrCmdAddData qrsAddData +#define qrCmdAddData2 qrsAddData2 +#define qrCmdOutputSymbol qrsOutputSymbols +#define qrCmdOutputSymbol2 qrsOutputSymbols2 + +/* others */ +#ifndef QRCMD_PROG_NAME +#define QRCMD_PROG_NAME "qrs" +#endif +#ifndef QR_SA_SRC_MAX +#define QR_SA_SRC_MAX 112233 +#endif +#define QRCMD_SRC_MAX QR_SA_SRC_MAX + +/* }}} */ +#else +/* {{{ disable structured append */ + +/* parameters */ +#define QRCMD_PTR_TYPE QRCode +#define QRCMD_EXTRA_PARAM_A +#define QRCMD_EXTRA_PARAM_B +#define QRCMD_EXTRA_PARAM_C +#define QRCMD_EXTRA_PARAM_D +#define QRCMD_MAX_NUM_A +#define QRCMD_MAX_NUM_B +#define QRCMD_DEFAULT_VERSION -1 + +/* functions */ +#define qrCmdInit qrInit +#define qrCmdDestroy qrDestroy +#define qrCmdFinalize qrFinalize +#define qrCmdGetErrorInfo qrGetErrorInfo +#define qrCmdAddData qrAddData +#define qrCmdAddData2 qrAddData2 +#define qrCmdOutputSymbol qrOutputSymbol +#define qrCmdOutputSymbol2 qrOutputSymbol2 + +/* others */ +#ifndef QRCMD_PROG_NAME +#define QRCMD_PROG_NAME "qr" +#endif +#define QRCMD_SRC_MAX QR_SRC_MAX + +/* }}} */ +#endif /* QRCMD_STRUCTURED_APPEND */ + +/* }}} */ +/* {{{ function prototype declarations */ + +QRCMD_PTR_TYPE * +qrGetParameter(int argc, char **argv, + int *fmt, int *sep, int *mag, QRCMD_EXTRA_PARAM_D char **output); + +void +qrShowHelp(void); + +/* }}} */ + +#endif /* _QRCMD_H_ */ diff --git a/kvm_system/main/lib/libqr/qrcnv.c b/kvm_system/main/lib/libqr/qrcnv.c new file mode 100644 index 0000000..519d94d --- /dev/null +++ b/kvm_system/main/lib/libqr/qrcnv.c @@ -0,0 +1,672 @@ +/* + * QR Code Generator Library: Symbol Converters for Basic Output Formats + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#include "qrcnv.h" + +/* {{{ utility macro */ + +#define repeat(m, n) for ((m) = 0; (m) < (n); (m)++) + +/* }}} */ +/* {{{ symbol writing macro */ + +#define qrInitRow(filler) { \ + memset(rbuf, (filler), (size_t)rsize); \ + rptr = rbuf; \ +} + +#define qrWriteRow(m, n) { \ + wsize = (int)(rptr - rbuf); \ + for ((m) = 0; (m) < (n); (m)++) { \ + memcpy(sptr, rbuf, (size_t)wsize); \ + sptr += wsize; \ + } \ + if (wsize < rsize) { \ + *size -= (rsize - wsize) * (n); \ + } \ +} + +/* + * シンボルを出力する際の定型作業をまとめたマクロ + * + * このマクロを呼び出す前に以下の4つのマクロをdefineし、 + * 呼び出した後はundefする + * qrWriteBOR() 行頭を書き込む + * qrWriteEOR() 行末を書き込む + * qrWriteBLM(m, n) 明モジュールを書き込む + * qrWriteDKM(m, n) 暗モジュールを書き込む +*/ +#define qrWriteSymbol(qr, filler) { \ + /* 分離パターン (上) */ \ + if (sepdim > 0) { \ + qrInitRow(filler); \ + qrWriteBOR(); \ + qrWriteBLM(j, imgdim); \ + qrWriteEOR(); \ + qrWriteRow(i, sepdim); \ + } \ + for (i = 0; i < dim; i++) { \ + /* 行を初期化 */ \ + qrInitRow(filler); \ + /* 行頭 */ \ + qrWriteBOR(); \ + /* 分離パターン (左) */ \ + qrWriteBLM(j, sepdim); \ + /* シンボル本体 */ \ + for (j = 0; j < dim; j++) { \ + if (qrIsBlack((qr), i, j)) { \ + qrWriteDKM(jx, mag); \ + } else { \ + qrWriteBLM(jx, mag); \ + } \ + } \ + /* 分離パターン (右) */ \ + qrWriteBLM(j, sepdim); \ + /* 行末 */ \ + qrWriteEOR(); \ + /* 行をmag回繰り返し書き込む */ \ + qrWriteRow(ix, mag); \ + } \ + /* 分離パターン (下) */ \ + if (sepdim > 0) { \ + qrInitRow(filler); \ + qrWriteBOR(); \ + qrWriteBLM(j, imgdim); \ + qrWriteEOR(); \ + qrWriteRow(i, sepdim); \ + } \ +} + +/* }}} */ +/* {{{ Structured append symbol writing macro */ + +#define qrsWriteSymbols(st, filler) { \ + for (k = 0; k < rows; k++) { \ + /* 分離パターン (上) */ \ + if (sepdim > 0) { \ + qrInitRow(filler); \ + qrWriteBOR(); \ + qrWriteBLM(j, xdim); \ + qrWriteEOR(); \ + qrWriteRow(i, sepdim); \ + } \ + for (i = 0; i < dim; i++) { \ + /* 行を初期化 */ \ + qrInitRow(filler); \ + /* 行頭 */ \ + qrWriteBOR(); \ + for (kx = 0; kx < cols; kx++) { \ + /* 分離パターン (左) */ \ + qrWriteBLM(j, sepdim); \ + /* シンボル本体 */ \ + if (order < 0) { \ + pos = k + rows * kx; \ + } else { \ + pos = cols * k + kx; \ + } \ + if (pos < (st)->num) { \ + for (j = 0; j < dim; j++) { \ + if (qrIsBlack((st)->qrs[pos], i, j)) { \ + qrWriteDKM(jx, mag); \ + } else { \ + qrWriteBLM(jx, mag); \ + } \ + } \ + } else { \ + qrWriteBLM(j, zdim); \ + } \ + } \ + /* 分離パターン (右) */ \ + qrWriteBLM(j, sepdim); \ + /* 行末 */ \ + qrWriteEOR(); \ + /* 行をmag回繰り返し書き込む */ \ + qrWriteRow(ix, mag); \ + } \ + } \ + /* 分離パターン (下) */ \ + if (sepdim > 0) { \ + qrInitRow(filler); \ + qrWriteBOR(); \ + qrWriteBLM(j, xdim); \ + qrWriteEOR(); \ + qrWriteRow(i, sepdim); \ + } \ +} + +/* }}} */ +/* {{{ Digit formatted symbol writing macro */ + +#define qrWriteBOR_Digit() +#define qrWriteEOR_Digit() { *rptr++ = ' '; } +#define qrWriteBLM_Digit(m, n) { rptr += (n); } +#define qrWriteDKM_Digit(m, n) { repeat(m, n) { *rptr++ = '1'; } } + +/* }}} */ +/* {{{ Ascii art formatted symbol writing macro */ + +#define qrWriteBOR_ASCII() +#ifdef WIN32 +#define qrWriteEOR_ASCII() { *rptr++ = '\r'; *rptr++ = '\n'; } +#else +#define qrWriteEOR_ASCII() { *rptr++ = '\n'; } +#endif +#ifdef _QRCNV_AA_STYLE_U2588 +#define QRCNV_AA_UNIT 3 +#define qrWriteBLM_ASCII(m, n) { rptr += (n) * 2; } +#define qrWriteDKM_ASCII(m, n) { repeat(m, n) { *rptr++ = 0xe2; *rptr++ = 0x96; *rptr++ = 0x88; } } +#else +#define QRCNV_AA_UNIT 2 +#define qrWriteBLM_ASCII(m, n) { rptr += (n) * 2; } +#define qrWriteDKM_ASCII(m, n) { repeat(m, n) { *rptr++ = 'X'; *rptr++ = 'X'; } } +#endif + +/* }}} */ +/* {{{ JSON formatted symbol writing macro */ + +#define qrWriteBOR_JSON() { *rptr++ = '['; } +#define qrWriteEOR_JSON() { rptr--; *rptr++ = ']'; rptr++; } +#ifdef _QR_JSON_STYLE_BOOLELAN +#define QRCNV_JSON_UNIT 6 +#define qrWriteBLM_JSON(m, n) { repeat(m, n) { memcpy(rptr, "false", 5); rptr += 6; } } +#define qrWriteDKM_JSON(m, n) { repeat(m, n) { memcpy(rptr, "true", 4); rptr += 5; } } +#else +#define QRCNV_JSON_UNIT 2 +#define qrWriteBLM_JSON(m, n) { repeat(m, n) { *rptr++ = '0'; rptr++; } } +#define qrWriteDKM_JSON(m, n) { repeat(m, n) { *rptr++ = '1'; rptr++; } } +#endif + +/* }}} */ +/* {{{ PBM formatted symbol writing macro */ + +#define qrWriteBOR_PBM() +#define qrWriteEOR_PBM() { *rptr++ = '\n'; } +#define qrWriteBLM_PBM(m, n) { repeat(m, n) { rptr++; *rptr++ = '0'; } } +#define qrWriteDKM_PBM(m, n) { repeat(m, n) { rptr++; *rptr++ = '1'; } } + +/* }}} */ +/* {{{ qrSymbolToDigit() */ + +/* + * 生成されたQRコードシンボルを0,1と空白で構成される文字列に変換する + */ +QR_API qr_byte_t * +qrSymbolToDigit(QRCode *qr, int sep, int mag, int *size) +{ + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, wsize; + int i, j, ix, jx, dim, imgdim, sepdim; + + QRCNV_CHECK_STATE(); + QRCNV_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + rsize = imgdim + 1; + *size = rsize * imgdim - 1; + QRCNV_MALLOC(rsize, *size + 1); + + sptr = sbuf; + +#define qrWriteBOR qrWriteBOR_Digit +#define qrWriteEOR qrWriteEOR_Digit +#define qrWriteBLM qrWriteBLM_Digit +#define qrWriteDKM qrWriteDKM_Digit + + /* + * シンボルを書き込む + */ + qrWriteSymbol(qr, '0'); + + /* + * 最後の文字(スペース)を終端文字に置換する + */ + *(--sptr) = '\0'; + +#undef qrWriteBOR +#undef qrWriteEOR +#undef qrWriteBLM +#undef qrWriteDKM + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrSymbolToASCII() */ + +/* + * 生成されたQRコードシンボルを0,1と空白で構成される文字列に変換する + */ +QR_API qr_byte_t * +qrSymbolToASCII(QRCode *qr, int sep, int mag, int *size) +{ + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, wsize; + int i, j, ix, jx, dim, imgdim, sepdim; + + QRCNV_CHECK_STATE(); + QRCNV_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + rsize = imgdim * QRCNV_AA_UNIT + QRCNV_EOL_SIZE; + *size = rsize * imgdim; + QRCNV_MALLOC(rsize, *size + 1); + + sptr = sbuf; + +#define qrWriteBOR qrWriteBOR_ASCII +#define qrWriteEOR qrWriteEOR_ASCII +#define qrWriteBLM qrWriteBLM_ASCII +#define qrWriteDKM qrWriteDKM_ASCII + + /* + * シンボルを書き込む + */ + qrWriteSymbol(qr, ' '); + + /* + * 終端文字を書き込む + */ + *sptr = '\0'; + +#undef qrWriteBOR +#undef qrWriteEOR +#undef qrWriteBLM +#undef qrWriteDKM + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrSymbolToJSON() */ + +/* + * 生成されたQRコードシンボルをJSON形式の文字列に変換する + * JSONをデコードすると二次元配列が得られる + */ +QR_API qr_byte_t * +qrSymbolToJSON(QRCode *qr, int sep, int mag, int *size) +{ + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, wsize; + int i, j, ix, jx, dim, imgdim, sepdim; + + QRCNV_CHECK_STATE(); + QRCNV_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + rsize = 1 + imgdim * QRCNV_JSON_UNIT + 1; + *size = 1 + rsize * imgdim - 1 + 1; + QRCNV_MALLOC(rsize, *size + 1); + + sptr = sbuf; + +#define qrWriteBOR qrWriteBOR_JSON +#define qrWriteEOR qrWriteEOR_JSON +#define qrWriteBLM qrWriteBLM_JSON +#define qrWriteDKM qrWriteDKM_JSON + + /* + * ヘッダを書き込む + */ + *sptr++ = '['; + + /* + * シンボルを書き込む + */ + qrWriteSymbol(qr, ','); + + /* + * フッタと終端文字を書き込む + */ + sptr--; + *sptr++ = ']'; + *sptr = '\0'; + +#undef qrWriteBOR +#undef qrWriteEOR +#undef qrWriteBLM +#undef qrWriteDKM + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrSymbolToPBM() */ + +/* + * 生成されたQRコードシンボルをモノクロ2値の + * アスキー形式Portable Bitmap(PBM)に変換する + */ +QR_API qr_byte_t * +qrSymbolToPBM(QRCode *qr, int sep, int mag, int *size) +{ + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, wsize; + int i, j, ix, jx, dim, imgdim, sepdim; + char header[64]; + int hsize; + + QRCNV_CHECK_STATE(); + QRCNV_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + hsize = snprintf(&(header[0]), sizeof(header), "P1\n%d %d\n", imgdim, imgdim); + if (hsize == -1 || header[hsize - 1] != '\n') { + QRCNV_RETURN_FAILURE(QR_ERR_UNKNOWN, _QR_FUNCTION); + } + rsize = imgdim * 2 + 1; + *size = hsize + rsize * imgdim; + QRCNV_MALLOC(rsize, *size + 1); + + sptr = sbuf; + +#define qrWriteBOR qrWriteBOR_PBM +#define qrWriteEOR qrWriteEOR_PBM +#define qrWriteBLM qrWriteBLM_PBM +#define qrWriteDKM qrWriteDKM_PBM + + /* + * ヘッダを書き込む + */ + memcpy(sptr, header, (size_t)hsize); + sptr += hsize; + + /* + * シンボルを書き込む + */ + qrWriteSymbol(qr, ' '); + + /* + * 終端文字を書き込む + */ + *sptr = '\0'; + +#undef qrWriteBOR +#undef qrWriteEOR +#undef qrWriteBLM +#undef qrWriteDKM + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrsSymbolsToDigit() */ + +/* + * 構造的連接用qrSymbolToDigit() + * order は無視される + */ +QR_API qr_byte_t * +qrsSymbolsToDigit(QRStructured *st, int sep, int mag, int order, int *size) +{ + QRCode *qr = st->cur; + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, wsize; + int i, j, k, ix, jx; + int cols, rows, xdim, ydim, zdim; + int dim, imgdim, sepdim; + + QRCNV_SA_CHECK_STATE(); + QRCNV_SA_IF_ONE(qrSymbolToDigit); + QRCNV_SA_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + rsize = imgdim + 1; + *size = rsize * imgdim * st->num - 1; + QRCNV_MALLOC(rsize, *size + 1); + + sptr = sbuf; + +#define qrWriteBOR qrWriteBOR_Digit +#define qrWriteEOR qrWriteEOR_Digit +#define qrWriteBLM qrWriteBLM_Digit +#define qrWriteDKM qrWriteDKM_Digit + + /* + * シンボルを書き込む + */ + for (k = 0; k < st->num; k++) { + qrWriteSymbol(st->qrs[k], '0'); + *(--sptr) = '\n'; + sptr++; + } + + /* + * 最後の文字(LF)を終端文字に置換する + */ + *(--sptr) = '\0'; + +#undef qrWriteBOR +#undef qrWriteEOR +#undef qrWriteBLM +#undef qrWriteDKM + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrsSymbolsToASCII() */ + +/* + * 構造的連接用qrSymbolToASCII() + */ +QR_API qr_byte_t * +qrsSymbolsToASCII(QRStructured *st, int sep, int mag, int order, int *size) +{ + QRCode *qr = st->cur; + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, wsize; + int i, j, k, ix, jx, kx; + int cols, rows, pos, xdim, ydim, zdim; + int dim, imgdim, sepdim; + + QRCNV_SA_CHECK_STATE(); + QRCNV_SA_IF_ONE(qrSymbolToASCII); + QRCNV_SA_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + rsize = xdim * QRCNV_AA_UNIT + QRCNV_EOL_SIZE; + *size = rsize * ydim; + QRCNV_MALLOC(rsize, *size + 1); + + sptr = sbuf; + +#define qrWriteBOR qrWriteBOR_ASCII +#define qrWriteEOR qrWriteEOR_ASCII +#define qrWriteBLM qrWriteBLM_ASCII +#define qrWriteDKM qrWriteDKM_ASCII + + /* + * シンボルを書き込む + */ + qrsWriteSymbols(st, ' '); + + /* + * 終端文字を書き込む + */ + *sptr = '\0'; + +#undef qrWriteBOR +#undef qrWriteEOR +#undef qrWriteBLM +#undef qrWriteDKM + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrsSymbolsToJSON() */ + +/* + * 構造的連接用qrSymbolToJSON() + * order は無視される + */ +QR_API qr_byte_t * +qrsSymbolsToJSON(QRStructured *st, int sep, int mag, int order, int *size) +{ + QRCode *qr = st->cur; + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, wsize; + int i, j, k, ix, jx; + int cols, rows, xdim, ydim, zdim; + int dim, imgdim, sepdim; + + QRCNV_SA_CHECK_STATE(); + /*QRCNV_SA_IF_ONE(qrSymbolToJSON);*/ + QRCNV_SA_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + rsize = 1 + imgdim * QRCNV_JSON_UNIT + 1; + *size = 1 + (1 + rsize * imgdim + 1) * st->num - 1 + 1; + QRCNV_MALLOC(rsize, *size + 1); + + sptr = sbuf; + +#define qrWriteBOR qrWriteBOR_JSON +#define qrWriteEOR qrWriteEOR_JSON +#define qrWriteBLM qrWriteBLM_JSON +#define qrWriteDKM qrWriteDKM_JSON + + /* + * ヘッダを書き込む + */ + *sptr++ = '['; + + /* + * シンボルを書き込む + */ + for (k = 0; k < st->num; k++) { + *sptr++ = '['; + qrWriteSymbol(st->qrs[k], ','); + sptr--; + *sptr++ = ']'; + *sptr++ = ','; + } + + /* + * フッタと終端文字を書き込む + */ + sptr--; + *sptr++ = ']'; + *sptr = '\0'; + +#undef qrWriteBOR +#undef qrWriteEOR +#undef qrWriteBLM +#undef qrWriteDKM + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrsSymbolsToPBM() */ + +/* + * 構造的連接用qrSymbolToPBM() + */ +QR_API qr_byte_t * +qrsSymbolsToPBM(QRStructured *st, int sep, int mag, int order, int *size) +{ + QRCode *qr = st->cur; + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, wsize; + int i, j, k, ix, jx, kx; + int cols, rows, pos, xdim, ydim, zdim; + int dim, imgdim, sepdim; + char header[64]; + int hsize; + + QRCNV_SA_CHECK_STATE(); + QRCNV_SA_IF_ONE(qrSymbolToPBM); + QRCNV_SA_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + hsize = snprintf(&(header[0]), 64, "P1\n%d %d\n", xdim, ydim); + if (hsize >= 64) { + QRCNV_RETURN_FAILURE(QR_ERR_UNKNOWN, _QR_FUNCTION); + } + rsize = xdim * 2 + 1; + *size = hsize + rsize * ydim; + QRCNV_MALLOC(rsize, *size + 1); + + sptr = sbuf; + +#define qrWriteBOR qrWriteBOR_PBM +#define qrWriteEOR qrWriteEOR_PBM +#define qrWriteBLM qrWriteBLM_PBM +#define qrWriteDKM qrWriteDKM_PBM + + /* + * ヘッダを書き込む + */ + memcpy(sptr, header, (size_t)hsize); + sptr += hsize; + + /* + * シンボルを書き込む + */ + qrsWriteSymbols(st, ' '); + + /* + * 終端文字を付加する + */ + *sptr = '\0'; + +#undef qrWriteBOR +#undef qrWriteEOR +#undef qrWriteBLM +#undef qrWriteDKM + + free(rbuf); + + return sbuf; +} + +/* }}} */ diff --git a/kvm_system/main/lib/libqr/qrcnv.h b/kvm_system/main/lib/libqr/qrcnv.h new file mode 100644 index 0000000..8be6d91 --- /dev/null +++ b/kvm_system/main/lib/libqr/qrcnv.h @@ -0,0 +1,165 @@ +/* + * QR Code Generator Library: Private Definitions for Symbol Converters + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#ifndef _QRCNV_H_ +#define _QRCNV_H_ + +/* {{{ include headers */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "qr.h" +#include "qr_util.h" +#include +#include +#include + +/* }}} */ +/* {{{ determine EOL size (CR+LF or LF) */ + +#ifdef WIN32 +#define QRCNV_EOL_SIZE 2 +#else +#define QRCNV_EOL_SIZE 1 +#endif + +/* }}} */ +/* {{{ error handlers */ + +#define QRCNV_RETURN_FAILURE(e, p) { \ + qrSetErrorInfo(qr, (e), (p)); \ + if (size) { \ + *size = -1; \ + } \ + return NULL; \ +} + +#define QRCNV_RETURN_FAILURE2(e, p) { \ + qrSetErrorInfo2(qr, (e), (p)); \ + if (size) { \ + *size = -1; \ + } \ + return NULL; \ +} + +#define QRCNV_RETURN_FAILURE3(e, p, ...) { \ + qrSetErrorInfo3(qr, (e), (p), __VA_ARGS__); \ + if (size) { \ + *size = -1; \ + } \ + return NULL; \ +} + +/* }}} */ +/* {{{ allocate memory for the working rowl and the symbo */ + +#define QRCNV_MALLOC(rsize, ssize) { \ + rbuf = (qr_byte_t *)malloc((size_t)(rsize)); \ + if (rbuf == NULL) { \ + QRCNV_RETURN_FAILURE2(QR_ERR_MEMORY_EXHAUSTED, _QR_FUNCTION); \ + } \ + sbuf = (qr_byte_t *)malloc((size_t)(ssize)); \ + if (sbuf == NULL) { \ + free(rbuf); \ + QRCNV_RETURN_FAILURE2(QR_ERR_MEMORY_EXHAUSTED, _QR_FUNCTION); \ + } \ +} + +/* }}} */ +/* {{{ check the state and the parameters */ + +#define QRCNV_CHECK_STATE() { \ + if (qr->state < QR_STATE_FINAL) { \ + QRCNV_RETURN_FAILURE(QR_ERR_STATE, _QR_FUNCTION); \ + } \ +} + +#define QRCNV_GET_SIZE() { \ + if (sep != -1 && (sep < 0 || mag > QR_SEP_MAX)) { \ + QRCNV_RETURN_FAILURE3(QR_ERR_INVALID_SEP, ": %d", sep); \ + } \ + if (mag <= 0 || mag > QR_MAG_MAX) { \ + QRCNV_RETURN_FAILURE3(QR_ERR_INVALID_MAG, ": %d", mag); \ + } \ + dim = qr_vertable[qr->param.version].dimension; \ + if (sep == -1) { \ + sepdim = QR_DIM_SEP * mag; \ + } else { \ + sepdim = sep * mag; \ + } \ + imgdim = dim * mag + sepdim * 2; \ +} + +/* }}} */ +/* {{{ check the state and the parameters (structured append) */ + +#define QRCNV_SA_CHECK_STATE() { \ + if (st->state < QR_STATE_FINAL) { \ + QRCNV_RETURN_FAILURE(QR_ERR_STATE, _QR_FUNCTION); \ + } \ +} + +#define QRCNV_SA_GET_SIZE() { \ + if (sep != -1 && (sep < 0 || mag > QR_SEP_MAX)) { \ + QRCNV_RETURN_FAILURE3(QR_ERR_INVALID_SEP, ": %d", sep); \ + } \ + if (mag <= 0 || mag > QR_MAG_MAX) { \ + QRCNV_RETURN_FAILURE3(QR_ERR_INVALID_MAG, ": %d", mag); \ + } \ + dim = qr_vertable[st->param.version].dimension; \ + if (sep == -1) { \ + sepdim = QR_DIM_SEP * mag; \ + } else { \ + sepdim = sep * mag; \ + } \ + zdim = dim * mag; \ + imgdim = zdim + sepdim * 2; \ + if (order > 0) { \ + if (st->num > order) { \ + cols = order; \ + rows = (st->num + order - 1) / cols; \ + } else { \ + cols = st->num; \ + rows = 1; \ + } \ + } else if (order < 0) { \ + int _a; \ + _a = abs(order); \ + if (st->num > _a) { \ + rows = _a; \ + cols = (st->num + _a - 1) / rows; \ + } else { \ + cols = 1; \ + rows = st->num; \ + } \ + } else { \ + double _r; \ + _r = sqrt((double)st->num); \ + cols = (int)ceil(_r); \ + rows = (int)floor(_r); \ + if (cols * rows < st->num) { \ + rows = (int)ceil(_r); \ + } \ + } \ + xdim = (zdim + sepdim) * cols + sepdim; \ + ydim = (zdim + sepdim) * rows + sepdim; \ +} + +#define QRCNV_SA_IF_ONE(func) { \ + if (st->num == 1) { \ + return (func)(st->qrs[0], sep, mag, size); \ + } \ +} + +#endif /* _QRCNV_H_ */ diff --git a/kvm_system/main/lib/libqr/qrcnv_bmp.c b/kvm_system/main/lib/libqr/qrcnv_bmp.c new file mode 100644 index 0000000..79a6cc3 --- /dev/null +++ b/kvm_system/main/lib/libqr/qrcnv_bmp.c @@ -0,0 +1,324 @@ +/* + * QR Code Generator Library: Symbol Converters for Windows Bitmap + * + * Core routines were originally written by Junn Ohta. + * Based on qr.c Version 0.1: 2004/4/3 (Public Domain) + * + * @package libqr + * @author Ryusuke SEKIYAMA + * @copyright 2006-2013 Ryusuke SEKIYAMA + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +#include "qrcnv.h" +#if defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__) +#include +#endif + +/* {{{ size constants */ + +#define QRCNV_BMP_BISIZE 40 /* Windows Bitmap */ +#define QRCNV_BMP_OFFBITS 62 /* 14(bf) + 40(bi) + 4(rgbq) + 4(rgbq) */ +#define QRCNV_BMP_PPM 3780 /* 96 dpi ~= 3780 ppm */ + /* 72 dpi ~= 2835 ppm */ + +/* }}} */ +/* {{{ utility macro */ + +#if defined(__BIG_ENDIAN__) +#define qrBmpWriteShort(ptr, n) { \ + uint16_t us = (uint16_t)((n) & 0xffffU); \ + qr_byte_t *tmp = (qr_byte_t *)&us; \ + *(ptr)++ = tmp[1]; \ + *(ptr)++ = tmp[0]; \ +} +#elif defined(__LITTLE_ENDIAN__) +#define qrBmpWriteShort(ptr, n) { \ + uint16_t us = (uint16_t)((n) & 0xffffU); \ + qr_byte_t *tmp = (qr_byte_t *)&us; \ + *(ptr)++ = tmp[0]; \ + *(ptr)++ = tmp[1]; \ +} +#else +#define qrBmpWriteShort(ptr, n) { \ + *(ptr)++ = (n) & 0xffU; \ + *(ptr)++ = ((n) >> 8) & 0xffU; \ +} +#endif + +#if defined(__BIG_ENDIAN__) +#define qrBmpWriteLong(ptr, n) { \ + uint32_t ul = (uint32_t)(n); \ + qr_byte_t *tmp = (qr_byte_t *)&ul; \ + *(ptr)++ = tmp[3]; \ + *(ptr)++ = tmp[2]; \ + *(ptr)++ = tmp[1]; \ + *(ptr)++ = tmp[0]; \ +} +#elif defined(__LITTLE_ENDIAN__) +#define qrBmpWriteLong(ptr, n) { \ + uint32_t ul = (uint32_t)(n); \ + qr_byte_t *tmp = (qr_byte_t *)&ul; \ + *(ptr)++ = tmp[0]; \ + *(ptr)++ = tmp[1]; \ + *(ptr)++ = tmp[2]; \ + *(ptr)++ = tmp[3]; \ +} +#else +#define qrBmpWriteLong(ptr, n) { \ + *(ptr)++ = (n) & 0xffU; \ + *(ptr)++ = ((n) >> 8) & 0xffU; \ + *(ptr)++ = ((n) >> 16) & 0xffU; \ + *(ptr)++ = ((n) >> 24) & 0xffU; \ +} +#endif + +#define qrBmpNextPixel() { \ + if (pxshift == 0) { \ + rptr++; \ + pxshift = 7; \ + } else { \ + pxshift--; \ + } \ +} + +/* }}} */ +/* {{{ function prototypes */ + +static qr_byte_t * +qrBmpWriteHeader(qr_byte_t *bof, int size, int width, int height, int imagesize); + +/* }}} */ +/* {{{ qrSymbolToBMP() */ + +/* + * 生成されたQRコードシンボルをモノクロ2値の + * Windows Bitmap(BMP)に変換する + */ +QR_API qr_byte_t * +qrSymbolToBMP(QRCode *qr, int sep, int mag, int *size) +{ + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, rmod, imgsize; + int sepskips, pxshift; + int i, j, ix, jx, dim, imgdim, sepdim; + + QRCNV_CHECK_STATE(); + QRCNV_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + rsize = (imgdim + 7) / 8; + if ((rmod = (rsize % 4)) != 0) { + rsize += 4 - rmod; + } + imgsize = rsize * imgdim; + *size = QRCNV_BMP_OFFBITS + imgsize; + QRCNV_MALLOC(rsize, *size); + + /* + * ヘッダを書き込む + */ + sptr = qrBmpWriteHeader(sbuf, *size, imgdim, imgdim, imgsize); + + /* + * シンボルを書き込む + */ + sepskips = rsize * sepdim; + /* 分離パターン (下) */ + if (sepskips) { + memset(sptr, 0, (size_t)sepskips); + sptr += sepskips; + } + for (i = dim - 1; i >= 0; i--) { + memset(rbuf, 0, (size_t)rsize); + pxshift = 7; + rptr = rbuf; + /* 分離パターン (左) */ + for (j = 0; j < sepdim; j++) { + qrBmpNextPixel(); + } + /* シンボル本体 */ + for (j = 0; j < dim; j++) { + if (qrIsBlack(qr, i, j)) { + for (jx = 0; jx < mag; jx++) { + *rptr |= 1 << pxshift; + qrBmpNextPixel(); + } + } else { + for (jx = 0; jx < mag; jx++) { + qrBmpNextPixel(); + } + } + } + /* 行をmag回繰り返し書き込む */ + for (ix = 0; ix < mag; ix++) { + memcpy(sptr, rbuf, (size_t)rsize); + sptr += rsize; + } + } + /* 分離パターン (上) */ + if (sepskips) { + memset(sptr, 0, (size_t)sepskips); + sptr += sepskips; + } + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrsSymbolsToBMP() */ + +/* + * 構造的連接用qrSymbolToBMP() + */ +QR_API qr_byte_t * +qrsSymbolsToBMP(QRStructured *st, int sep, int mag, int order, int *size) +{ + QRCode *qr = st->cur; + qr_byte_t *rbuf, *rptr; + qr_byte_t *sbuf, *sptr; + int rsize, rmod, imgsize; + int sepskips, pxshift; + int i, j, k, ix, jx, kx; + int cols, rows, pos, xdim, ydim, zdim; + int dim, imgdim, sepdim; + + QRCNV_SA_CHECK_STATE(); + QRCNV_SA_IF_ONE(qrSymbolToBMP); + QRCNV_SA_GET_SIZE(); + + /* + * 変換後のサイズを計算し、メモリを確保する + */ + rsize = (xdim + 7) / 8; + if ((rmod = (rsize % 4)) != 0) { + rsize += 4 - rmod; + } + imgsize = rsize * ydim; + *size = QRCNV_BMP_OFFBITS + imgsize; + QRCNV_MALLOC(rsize, *size); + + /* + * ヘッダを書き込む + */ + sptr = qrBmpWriteHeader(sbuf, *size, xdim, ydim, imgsize); + + /* + * シンボルを書き込む + */ + sepskips = rsize * sepdim; + for (k = rows - 1; k >= 0; k--) { + /* 分離パターン (下) */ + if (sepskips) { + memset(sptr, 0, (size_t)sepskips); + sptr += sepskips; + } + for (i = dim - 1; i >= 0; i--) { + memset(rbuf, 0, (size_t)rsize); + pxshift = 7; + rptr = rbuf; + for (kx = 0; kx < cols; kx++) { + /* 分離パターン (左) */ + for (j = 0; j < sepdim; j++) { + qrBmpNextPixel(); + } + /* シンボル本体 */ + if (order < 0) { + pos = k + rows * kx; + } else { + pos = cols * k + kx; + } + if (pos >= st->num) { + break; + } + for (j = 0; j < dim; j++) { + if (qrIsBlack(st->qrs[pos], i, j)) { + for (jx = 0; jx < mag; jx++) { + *rptr |= 1 << pxshift; + qrBmpNextPixel(); + } + } else { + for (jx = 0; jx < mag; jx++) { + qrBmpNextPixel(); + } + } + } + } + /* 行をmag回繰り返し書き込む */ + for (ix = 0; ix < mag; ix++) { + memcpy(sptr, rbuf, (size_t)rsize); + sptr += rsize; + } + } + } + /* 分離パターン (上) */ + if (sepskips) { + memset(sptr, 0, (size_t)sepskips); + sptr += sepskips; + } + + free(rbuf); + + return sbuf; +} + +/* }}} */ +/* {{{ qrBmpWriteHeader() */ + +static qr_byte_t * +qrBmpWriteHeader(qr_byte_t *bof, int size, int width, int height, int imagesize) +{ + qr_byte_t *ptr; + + ptr = bof; + + /* + * BITMAPFILEHEADER + */ + *ptr++ = 'B'; /* bfType */ + *ptr++ = 'M'; /* bfType */ + qrBmpWriteLong(ptr, size); /* bfSize */ + qrBmpWriteShort(ptr, 0); /* bfReserved1 */ + qrBmpWriteShort(ptr, 0); /* bfReserved2 */ + qrBmpWriteLong(ptr, QRCNV_BMP_OFFBITS); /* bfOffBits */ + + /* + * BMPINFOHEADER + */ + qrBmpWriteLong(ptr, QRCNV_BMP_BISIZE); /* biSize */ + qrBmpWriteLong(ptr, width); /* biWidth */ + qrBmpWriteLong(ptr, height); /* biHeight */ + qrBmpWriteShort(ptr, 1); /* biPlanes */ + qrBmpWriteShort(ptr, 1); /* biBitCount - 1 bit per pixel */ + qrBmpWriteLong(ptr, 0); /* biCopmression - BI_RGB */ + qrBmpWriteLong(ptr, imagesize); /* biSizeImage */ + qrBmpWriteLong(ptr, QRCNV_BMP_PPM); /* biXPixPerMeter */ + qrBmpWriteLong(ptr, QRCNV_BMP_PPM); /* biYPixPerMeter */ + qrBmpWriteLong(ptr, 2); /* biClrUsed */ + qrBmpWriteLong(ptr, 2); /* biCirImportant */ + + /* + * RGBQUAD - white + */ + *ptr++ = 255; /* rgbBlue */ + *ptr++ = 255; /* rgbGreen */ + *ptr++ = 255; /* rgbRed */ + *ptr++ = 0; /* rgbReserved */ + + /* + *RGBQUAD - black + */ + *ptr++ = 0; /* rgbBlue */ + *ptr++ = 0; /* rgbGreen */ + *ptr++ = 0; /* rgbRed */ + *ptr++ = 0; /* rgbReserved */ + + return ptr; +} + +/* }}} */ diff --git a/kvm_system/main/lib/oled_ctrl/oled_ctrl.cpp b/kvm_system/main/lib/oled_ctrl/oled_ctrl.cpp new file mode 100644 index 0000000..994edc4 --- /dev/null +++ b/kvm_system/main/lib/oled_ctrl/oled_ctrl.cpp @@ -0,0 +1,682 @@ +#include "oled_ctrl.h" + +using namespace maix; +using namespace maix::sys; +using namespace maix::peripheral; +i2c::I2C oled_alpha(1, i2c::Mode::MASTER); +i2c::I2C oled_beta(5, i2c::Mode::MASTER); + +uint8_t OLED_state = 0; +uint8_t kvm_hw_ver = 0; + +/* mode = OLED_CMD + * = OLED_DATA */ +void oled_write_register(uint8_t mode, uint8_t data) +{ + if(OLED_state){ + uint8_t buf[2]; + + buf[0] = mode; + buf[1] = data; + if(kvm_hw_ver == 0){ + if(oled_alpha.writeto(OLED_ADDR, buf, 2) == (int)-err::Err::ERR_IO){ + return; + } + } else if(kvm_hw_ver == 1){ + if(oled_beta.writeto(OLED_ADDR, buf, 2) == (int)-err::Err::ERR_IO){ + return; + } + } else if(kvm_hw_ver == 2){ + if(oled_beta.writeto(OLED_PCIe_ADDR, buf, 2) == (int)-err::Err::ERR_IO){ + return; + } + } + return; + } + return; +} + +int oled_exist(void) +{ + // gpio::GPIO oled_rst("GPIOP19", gpio::Mode::OUT, gpio::Pull::PULL_UP); + // oled_rst.high(); + // system("devmem 0x030010D4 32 0x3"); + // system("devmem 0x030010D0 32 0x2"); + // system("devmem 0x030010DC 32 0x2"); + + if(access("/etc/kvm/hw", F_OK) == 0){ + uint8_t RW_Data[2]; + FILE *fp = fopen("/etc/kvm/hw", "r"); + fread(RW_Data, sizeof(char), 2, fp); + fclose(fp); + if(RW_Data[0] == 'b') kvm_hw_ver = 1; + else if(RW_Data[0] == 'p') kvm_hw_ver = 2; + } + + uint8_t buf[2]; + + buf[0] = OLED_CMD; + buf[1] = 0xAE; + if(kvm_hw_ver == 0){ + if(oled_alpha.writeto(OLED_ADDR, buf, 2) == (int)-err::Err::ERR_IO){ + return 0; + } + } else if(kvm_hw_ver == 1){ + printf("beta\r\n"); + if(oled_beta.writeto(OLED_ADDR, buf, 2) == (int)-err::Err::ERR_IO){ + return 0; + } + } else if(kvm_hw_ver == 2){ + printf("PCIe\r\n"); + if(oled_beta.writeto(OLED_PCIe_ADDR, buf, 2) == (int)-err::Err::ERR_IO){ + return 0; + } + } + return 1; +} + +// 坐标设置 +void OLED_Set_Pos(uint8_t x, uint8_t y) +{ + // oled_write_register(OLED_CMD, 0xb0+y); + // oled_write_register(OLED_CMD, ((x&0xf0)>>4)|0x10); + // oled_write_register(OLED_CMD, (x&0x0f)); + if(kvm_hw_ver == 2){ + x += 32; + y += 8; + } + oled_write_register(OLED_CMD, 0xb0+y); + oled_write_register(OLED_CMD, ((x&0xf0)>>4)|0x10); + oled_write_register(OLED_CMD, (x&0x0f)); +} + +//开启OLED显示 +void OLED_Display_On() +{ + oled_write_register(OLED_CMD, 0X8D); + oled_write_register(OLED_CMD, 0X14); + oled_write_register(OLED_CMD, 0XAF); +} + +//关闭OLED显示 +void OLED_Display_Off() +{ + oled_write_register(OLED_CMD, 0X8D); + oled_write_register(OLED_CMD, 0X10); + oled_write_register(OLED_CMD, 0XAE); +} + +//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! +void OLED_Clear(void) +{ + uint8_t i,n; + for(i=0;i<8;i++) + { + oled_write_register(OLED_CMD, 0xb0+i); + oled_write_register(OLED_CMD, 0x00); + oled_write_register(OLED_CMD, 0x10); + for(n=0;n<128;n++)oled_write_register(OLED_DATA, 0x00); + } //更新显示 +} + +void OLED_Fill(void) +{ + uint8_t i,n; + for(i=0;i<8;i++) + { + oled_write_register(OLED_CMD, 0xb0+i); + oled_write_register(OLED_CMD, 0x00); + oled_write_register(OLED_CMD, 0x10); + for(n=0;n<128;n++)oled_write_register(OLED_DATA, 0xFF); + } //更新显示 +} + +//在指定位置显示一个字符,包括部分字符 +//x:0~127 +//y:0~63 +//sizey:选择字体 6x8 8x16 +void OLED_ShowChar(uint8_t x,uint8_t y,char chr,uint8_t sizey) +{ + uint8_t c=0,sizex=sizey/2; + uint16_t i=0,size1; + if(sizey==8)size1=6; + else if(sizey==4)size1=4; + else size1=(sizey/8+((sizey%8)?1:0))*(sizey/2); + c=chr-' ';//得到偏移后的值 + OLED_Set_Pos(x, y); + for(i=0; i +#include +#include +#include +#include + +#define OLED_DISABLE 0 +#define OLED_ENABLE 1 +#define OLED_ADDR 0x3D +#define OLED_PCIe_ADDR 0x3C +#define OLED_CMD 0x00 +#define OLED_DATA 0x40 + +#define HDMI_STATE 0x01 +#define HID_STATE 0x02 +#define ETH_STATE 0x04 +#define WIFI_STATE 0x08 + +#define KVM_INIT 0x00 +#define KVM_ETH_IP 0x01 +#define KVM_WIFI_IP 0x02 +#define KVM_HDMI_STATE 0x03 +#define KVM_HDMI_RES 0x04 +#define KVM_STEAM_TYPE 0x05 +#define KVM_STEAM_FPS 0x06 +#define KVM_JPG_QLTY 0x07 +#define KVM_CPU_IDLE 0x08 + +#define KVM_RES_none 0x00 +#define KVM_RES_480P 0x01 +#define KVM_RES_600P 0x02 +#define KVM_RES_720P 0x03 +#define KVM_RES_1080P 0x04 + +#define KVM_TYPE_none 0x00 +#define KVM_TYPE_MJPG 0x01 +#define KVM_TYPE_H264 0x02 + +#define AlignRightEND 127 +#define AlignRightEND_P 63 + +extern uint8_t OLED_state; +extern uint8_t kvm_hw_ver; + +/************************************6*8的点阵************************************/ +const uint8_t oled_asc2_0806[][6] ={ +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},// sp +{0x00, 0x00, 0x00, 0x2f, 0x00, 0x00},// ! +{0x00, 0x00, 0x07, 0x00, 0x07, 0x00},// " +{0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14},// # +{0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12},// $ +{0x00, 0x23, 0x13, 0x08, 0x64, 0x62},// % +{0x00, 0x36, 0x49, 0x55, 0x22, 0x50},// & +{0x00, 0x00, 0x05, 0x03, 0x00, 0x00},// ' +{0x00, 0x00, 0x1c, 0x22, 0x41, 0x00},// ( +{0x00, 0x00, 0x41, 0x22, 0x1c, 0x00},// ) +// {0x00, 0x14, 0x08, 0x3E, 0x08, 0x14},// * +{0x00, 0x00, 0x28, 0x10, 0x28, 0x00},// *->x +{0x00, 0x08, 0x08, 0x3E, 0x08, 0x08},// + +{0x00, 0x00, 0x00, 0xA0, 0x60, 0x00},// , +{0x00, 0x08, 0x08, 0x08, 0x08, 0x08},// - +{0x00, 0x00, 0x60, 0x60, 0x00, 0x00},// . +{0x00, 0x20, 0x10, 0x08, 0x04, 0x02},// / +{0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E},// 0 +{0x00, 0x00, 0x42, 0x7F, 0x40, 0x00},// 1 +{0x00, 0x42, 0x61, 0x51, 0x49, 0x46},// 2 +{0x00, 0x21, 0x41, 0x45, 0x4B, 0x31},// 3 +{0x00, 0x18, 0x14, 0x12, 0x7F, 0x10},// 4 +{0x00, 0x27, 0x45, 0x45, 0x45, 0x39},// 5 +{0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30},// 6 +{0x00, 0x01, 0x71, 0x09, 0x05, 0x03},// 7 +{0x00, 0x36, 0x49, 0x49, 0x49, 0x36},// 8 +{0x00, 0x06, 0x49, 0x49, 0x29, 0x1E},// 9 +{0x00, 0x00, 0x36, 0x36, 0x00, 0x00},// : +{0x00, 0x00, 0x56, 0x36, 0x00, 0x00},// ; +{0x00, 0x08, 0x14, 0x22, 0x41, 0x00},// < +{0x00, 0x14, 0x14, 0x14, 0x14, 0x14},// = +{0x00, 0x00, 0x41, 0x22, 0x14, 0x08},// > +{0x00, 0x02, 0x01, 0x51, 0x09, 0x06},// ? +{0x00, 0x32, 0x49, 0x59, 0x51, 0x3E},// @ +{0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C},// A +{0x00, 0x7F, 0x49, 0x49, 0x49, 0x36},// B +{0x00, 0x3E, 0x41, 0x41, 0x41, 0x22},// C +{0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C},// D +{0x00, 0x7F, 0x49, 0x49, 0x49, 0x41},// E +{0x00, 0x7F, 0x09, 0x09, 0x09, 0x01},// F +{0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A},// G +{0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F},// H +{0x00, 0x00, 0x41, 0x7F, 0x41, 0x00},// I +{0x00, 0x20, 0x40, 0x41, 0x3F, 0x01},// J +{0x00, 0x7F, 0x08, 0x14, 0x22, 0x41},// K +{0x00, 0x7F, 0x40, 0x40, 0x40, 0x40},// L +{0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F},// M +{0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F},// N +{0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E},// O +{0x00, 0x7F, 0x09, 0x09, 0x09, 0x06},// P +{0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E},// Q +{0x00, 0x7F, 0x09, 0x19, 0x29, 0x46},// R +{0x00, 0x46, 0x49, 0x49, 0x49, 0x31},// S +{0x00, 0x01, 0x01, 0x7F, 0x01, 0x01},// T +{0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F},// U +{0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F},// V +{0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F},// W +{0x00, 0x63, 0x14, 0x08, 0x14, 0x63},// X +{0x00, 0x07, 0x08, 0x70, 0x08, 0x07},// Y +{0x00, 0x61, 0x51, 0x49, 0x45, 0x43},// Z +{0x00, 0x00, 0x7F, 0x41, 0x41, 0x00},// [ +{0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55},// 55 +{0x00, 0x00, 0x41, 0x41, 0x7F, 0x00},// ] +{0x00, 0x04, 0x02, 0x01, 0x02, 0x04},// ^ +{0x00, 0x40, 0x40, 0x40, 0x40, 0x40},// _ +{0x00, 0x00, 0x01, 0x02, 0x04, 0x00},// ' +{0x00, 0x20, 0x54, 0x54, 0x54, 0x78},// a +{0x00, 0x7F, 0x48, 0x44, 0x44, 0x38},// b +{0x00, 0x38, 0x44, 0x44, 0x44, 0x20},// c +{0x00, 0x38, 0x44, 0x44, 0x48, 0x7F},// d +{0x00, 0x38, 0x54, 0x54, 0x54, 0x18},// e +{0x00, 0x08, 0x7E, 0x09, 0x01, 0x02},// f +{0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C},// g +{0x00, 0x7F, 0x08, 0x04, 0x04, 0x78},// h +{0x00, 0x00, 0x44, 0x7D, 0x40, 0x00},// i +{0x00, 0x40, 0x80, 0x84, 0x7D, 0x00},// j +{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00},// k +{0x00, 0x00, 0x41, 0x7F, 0x40, 0x00},// l +{0x00, 0x7C, 0x04, 0x18, 0x04, 0x78},// m +{0x00, 0x7C, 0x08, 0x04, 0x04, 0x78},// n +{0x00, 0x38, 0x44, 0x44, 0x44, 0x38},// o +{0x00, 0xFC, 0x24, 0x24, 0x24, 0x18},// p +{0x00, 0x18, 0x24, 0x24, 0x18, 0xFC},// q +{0x00, 0x7C, 0x08, 0x04, 0x04, 0x08},// r +{0x00, 0x48, 0x54, 0x54, 0x54, 0x20},// s +{0x00, 0x04, 0x3F, 0x44, 0x40, 0x20},// t +{0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C},// u +{0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C},// v +{0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C},// w +{0x00, 0x44, 0x28, 0x10, 0x28, 0x44},// x +{0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C},// y +{0x00, 0x44, 0x64, 0x54, 0x4C, 0x44},// z +{0x14, 0x14, 0x14, 0x14, 0x14, 0x14},// horiz lines + +}; + +const uint8_t oled_asc2_0804[][4] ={ +{0x00,0x00,0x00,0x00},// sp +{0x7c,0x52,0x7c,0x00},// ! +{0x00,0x0c,0x00,0x0c},// " +{0x00,0x7c,0x28,0x7c},// # +{0x00,0x5c,0x5e,0x74},// $ +{0x6c,0x30,0x18,0x6c},// % +{0x00,0x68,0x54,0x68},// & +{0x00,0x00,0x0c,0x00},// ' +{0x00,0x38,0x44,0x00},// ( +{0x00,0x44,0x38,0x00},// ) +{0x00,0x00,0x30,0x00},// * +{0x00,0x10,0x38,0x10},// + +{0x00,0x00,0x60,0x00},// , +{0x00,0x10,0x10,0x10},// - +{0x00,0x00,0x40,0x00},// . +{0x00,0x40,0x38,0x04},// / +{0x00,0x7c,0x44,0x7c},// 0 +{0x00,0x44,0x7c,0x40},// 1 +{0x00,0x74,0x54,0x5c},// 2 +{0x00,0x54,0x54,0x7c},// 3 +{0x00,0x1c,0x10,0x7c},// 4 +{0x00,0x5c,0x54,0x74},// 5 +{0x00,0x7c,0x54,0x74},// 6 +{0x00,0x04,0x04,0x7c},// 7 +{0x00,0x7c,0x54,0x7c},// 8 +{0x00,0x5c,0x54,0x7c},// 9 +{0x00,0x00,0x28,0x00},// : +{0x00,0x40,0x28,0x00},// ; +{0x00,0x10,0x28,0x44},// < +{0x00,0x28,0x28,0x28},// = +{0x00,0x44,0x28,0x10},// > +{0x00,0x04,0x54,0x0c},// ? +{0x38,0x64,0x54,0x78},// @ +{0x00,0x78,0x14,0x78},// A +{0x00,0x7c,0x54,0x38},// B +{0x00,0x38,0x44,0x44},// C +{0x00,0x7c,0x44,0x38},// D +{0x00,0x7c,0x54,0x54},// E +{0x00,0x7c,0x14,0x14},// F +{0x00,0x6c,0x54,0x74},// G +{0x00,0x7c,0x10,0x7c},// H +{0x00,0x44,0x7c,0x44},// I +{0x00,0x44,0x7c,0x04},// J +{0x00,0x7c,0x10,0x6c},// K +{0x00,0x7c,0x40,0x40},// L +{0x00,0x7c,0x18,0x7c},// M +{0x00,0x6c,0x18,0x74},// N +{0x00,0x38,0x44,0x38},// O +{0x00,0x7c,0x14,0x1c},// P +{0x00,0x3c,0x24,0x7c},// Q +{0x00,0x7c,0x14,0x6c},// R +{0x00,0x58,0x54,0x64},// S +{0x00,0x04,0x7c,0x04},// T +{0x00,0x3c,0x40,0x7c},// U +{0x00,0x3c,0x60,0x3c},// V +{0x00,0x7c,0x30,0x7c},// W +{0x00,0x6c,0x10,0x6c},// X +{0x00,0x0c,0x70,0x0c},// Y +{0x00,0x64,0x54,0x4c},// Z +{0x00,0x7c,0x44,0x00},// [ +{0x28,0x54,0x28,0x54},// 55 +{0x00,0x44,0x7c,0x00},// ] +}; + +//16*16 ASCII字符集点阵 +const uint8_t oled_asc2_1608[][16]={ +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/ +{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},/*"!",1*/ +{0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*""",2*/ +{0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00},/*"#",3*/ +{0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00},/*"$",4*/ +{0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00},/*"%",5*/ +{0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10},/*"&",6*/ +{0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/ +{0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},/*"(",8*/ +{0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},/*")",9*/ +{0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},/*"*",10*/ +{0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00},/*"+",11*/ +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00},/*",",12*/ +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01},/*"-",13*/ +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},/*".",14*/ +{0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00},/*"/",15*/ +{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},/*"0",16*/ +{0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"1",17*/ +{0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},/*"2",18*/ +{0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},/*"3",19*/ +{0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},/*"4",20*/ +{0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},/*"5",21*/ +{0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},/*"6",22*/ +{0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},/*"7",23*/ +{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},/*"8",24*/ +{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00},/*"9",25*/ +{0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},/*":",26*/ +{0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00},/*";",27*/ +{0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},/*"<",28*/ +{0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00},/*"=",29*/ +{0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},/*">",30*/ +{0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00},/*"?",31*/ +{0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00},/*"@",32*/ +{0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},/*"A",33*/ +{0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},/*"B",34*/ +{0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},/*"C",35*/ +{0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},/*"D",36*/ +{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},/*"E",37*/ +{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},/*"F",38*/ +{0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},/*"G",39*/ +{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},/*"H",40*/ +{0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"I",41*/ +{0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},/*"J",42*/ +{0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},/*"K",43*/ +{0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},/*"L",44*/ +{0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00},/*"M",45*/ +{0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},/*"N",46*/ +{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},/*"O",47*/ +{0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},/*"P",48*/ +{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00},/*"Q",49*/ +{0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},/*"R",50*/ +{0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},/*"S",51*/ +{0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},/*"T",52*/ +{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},/*"U",53*/ +{0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},/*"V",54*/ +{0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00},/*"W",55*/ +{0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},/*"X",56*/ +{0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},/*"Y",57*/ +{0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},/*"Z",58*/ +{0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},/*"[",59*/ +{0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},/*"\",60*/ +{0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},/*"]",61*/ +{0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"^",62*/ +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},/*"_",63*/ +{0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/ +{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20},/*"a",65*/ +{0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},/*"b",66*/ +{0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},/*"c",67*/ +{0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20},/*"d",68*/ +{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00},/*"e",69*/ +{0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"f",70*/ +{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},/*"g",71*/ +{0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},/*"h",72*/ +{0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"i",73*/ +{0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},/*"j",74*/ +{0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00},/*"k",75*/ +{0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"l",76*/ +{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},/*"m",77*/ +{0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},/*"n",78*/ +{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},/*"o",79*/ +{0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00},/*"p",80*/ +{0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80},/*"q",81*/ +{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},/*"r",82*/ +{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},/*"s",83*/ +{0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00},/*"t",84*/ +{0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},/*"u",85*/ +{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00},/*"v",86*/ +{0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00},/*"w",87*/ +{0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00},/*"x",88*/ +{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00},/*"y",89*/ +{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},/*"z",90*/ +{0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40},/*"{",91*/ +{0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},/*"|",92*/ +{0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00},/*"}",93*/ +{0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"~",94*/ +}; + +// [!] +const uint8_t etherror[6] +{0x00,0x3e,0x7f,0x51,0x7f,0x3e}; // [!] + +//6*xx 图标点阵 +const uint8_t oled_kvm_0621[][21]{ +{0x00,0x3e,0x08,0x08,0x3e,0x00,0x3e,0x22,0x22,0x1c,0x00,0x3c,0x02,0x0c,0x02,0x3c,0x00,0x22,0x3e,0x22,0x00},/*"HDMI=0",0*,21*/ +{0x3e,0x41,0x77,0x77,0x41,0x7f,0x41,0x5d,0x5d,0x63,0x7f,0x43,0x7d,0x73,0x7d,0x43,0x7f,0x5d,0x41,0x5d,0x3e},/*"HDMI=1",0*,21*/ +{0x00,0x3e,0x08,0x08,0x3e,0x00,0x22,0x3e,0x20,0x00,0x3e,0x22,0x22,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"USB=0",0, 15*/ +{0x3e,0x41,0x77,0x77,0x41,0x7f,0x5d,0x41,0x5f,0x7f,0x41,0x5d,0x5d,0x63,0x3e,0x00,0x00,0x00,0x00,0x00,0x00},/*"USB=1",1, 15*/ +{0x00,0x3e,0x2a,0x2a,0x22,0x00,0x02,0x3e,0x02,0x00,0x3e,0x08,0x08,0x3e,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"ETH=0",1, 15*/ +{0x3e,0x41,0x55,0x55,0x5d,0x7f,0x7d,0x41,0x7d,0x7f,0x41,0x77,0x77,0x41,0x3e,0x00,0x00,0x00,0x00,0x00,0x00},/*"ETH=1",1, 15*/ +{0x00,0x1e,0x20,0x18,0x20,0x1e,0x00,0x3a,0x00,0x3e,0x0a,0x0a,0x00,0x3a,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"WiFi=0",1, 15*/ +{0x3e,0x61,0x5f,0x67,0x5f,0x61,0x7f,0x45,0x7f,0x41,0x75,0x75,0x7f,0x45,0x3e,0x00,0x00,0x00,0x00,0x00,0x00},/*"WiFi=1",1, 15*/ +}; +const uint8_t oled_kvm_logo[116] = +{ + 0xf8,0x1f,0xfc,0x3f,0xf6,0x67,0x06,0x60,0xce,0x6f,0x3e,0x7f,0xfe,0x7c,0xf6,0x73,0x06,0x60,0xe6,0x6f, + 0xfe,0x7f,0xfe,0x71,0xfe,0x6e,0xfe,0x6e,0xfe,0x61,0xfe,0x7f,0xfe,0x60,0xfe,0x7e,0xfe,0x7e,0xfe,0x60, + 0xfe,0x7f,0xfe,0x71,0xfe,0x6e,0xfe,0x6e,0xfe,0x71,0xfe,0x7f,0xf6,0x6f,0x06,0x60,0x76,0x6f,0x3e,0x7e, + 0x8e,0x79,0xe6,0x63,0xfe,0x6f,0xf6,0x7f,0x06,0x7f,0x7e,0x78,0xfe,0x63,0xfe,0x79,0x3e,0x7e,0x86,0x7f, + 0xf6,0x6f,0x7e,0x60,0x86,0x6f,0x86,0x7f,0x7e,0x7c,0x86,0x7f,0x86,0x6f,0x7e,0x60,0xfe,0x6f,0xfc,0x3f, + 0xf8,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x3f,0xfc,0x3f +}; +const uint8_t oled_sipeed_logo[32] = { +0x00,0x00,0xf8,0x1f,0xfc,0x3f,0xf6,0x7f,0xb2,0x7f,0x1e,0x67,0x0e,0x66,0x66,0x66, +0x66,0x66,0x66,0x66,0x66,0x70,0xfe,0x78,0xfe,0x7d,0xfc,0x3f,0xf8,0x1f,0x00,0x00 +}; +int oled_exist(void); +void OLED_Clear(void); +void OLED_Fill(void); +void OLED_Init(void); +void OLED_Revolve(void); +void OLED_ShowState(uint8_t x,uint8_t y,char chr,uint8_t size); +void OLED_DisplayTurn(uint8_t i); +void OLED_ColorTurn(uint8_t i); +void OLED_ShowError(uint8_t x,uint8_t y,uint8_t _state); +void OLED_ShowCharTurn(uint8_t x,uint8_t y,char chr,uint8_t sizey); +void OLED_ShowNum(uint8_t x, uint8_t y, uint8_t num, uint8_t len, uint8_t sizey); +void OLED_ShowString(uint8_t x, uint8_t y, char *chr, uint8_t sizey); +void OLED_ShowStringTurn(uint8_t x, uint8_t y, char *chr, uint8_t sizey); +void OLED_ShowStringtoend(uint8_t x, uint8_t y, char *chr, uint8_t sizey, uint8_t end); +void OLED_ShowString_AlignRight(uint8_t x_end, uint8_t y, char *chr, uint8_t size); +void OLED_ROW(uint8_t _EN); +void OLED_ShowLogo(); +void OLED_ShowSipeedLogo(); +void OLED_Showline(); +void OLED_Showline_1(); +void OLED_ShowIMG(uint8_t x,uint8_t y,char *chr,uint8_t width,uint8_t height); +void OLED_ShowKVMStreamState(uint8_t kvm_state_s, void* pdata); +void OLED_Show_Res(uint16_t _w, uint16_t _h); +void OLED_ShowKVMState(uint8_t _Interface, int8_t _EN); +void OLED_Show_Network_Error(uint8_t _state); + +#endif // OLED_CTRL_H_ \ No newline at end of file diff --git a/kvm_system/main/lib/oled_ui/oled_ui.cpp b/kvm_system/main/lib/oled_ui/oled_ui.cpp new file mode 100644 index 0000000..74923fd --- /dev/null +++ b/kvm_system/main/lib/oled_ui/oled_ui.cpp @@ -0,0 +1,506 @@ +#include "oled_ui.h" + +using namespace maix; +using namespace maix::sys; + +extern kvm_sys_state_t kvm_sys_state; +extern kvm_oled_state_t kvm_oled_state; + +void kvm_init_cube_ui(void) +{ + uint8_t temp; + char* str_temp = "192.168.1.243"; + + OLED_Clear(); + // OLED_Revolve(); + OLED_ShowLogo(); + OLED_ShowSipeedLogo(); + OLED_ShowKVMState(HDMI_STATE, 0); + OLED_ShowKVMState(HID_STATE, 0); + OLED_ShowKVMState(ETH_STATE, 0); + OLED_ShowKVMState(WIFI_STATE, 0); + OLED_Showline(); + OLED_ShowKVMStreamState(KVM_INIT, &temp); + temp = 0; + OLED_ShowKVMStreamState(KVM_ETH_IP, &temp); + temp = KVM_RES_none; + OLED_ShowKVMStreamState(KVM_HDMI_RES, &temp); + temp = KVM_TYPE_none; + OLED_ShowKVMStreamState(KVM_STEAM_TYPE, &temp); + temp = 0; + OLED_ShowKVMStreamState(KVM_STEAM_FPS, &temp); + temp = 80; + OLED_ShowKVMStreamState(KVM_JPG_QLTY, &temp); +} + +void kvm_init_pcie_ui(void) +{ + OLED_Revolve(); + OLED_Showline_1(); + OLED_ShowSipeedLogo(); + OLED_ShowKVMState(HDMI_STATE, 0); + OLED_ShowKVMState(HID_STATE, 0); + OLED_ShowKVMState(ETH_STATE, 0); + OLED_ShowKVMState(WIFI_STATE, 0); + + // OLED_ShowString(0, 2, "!192.168.222.197", 4); + // OLED_ShowString(1, 2, " 192.168.2.197", 4); + // OLED_ShowString(1, 3, "1920*1080", 4); + // OLED_ShowString(41, 3, " FPS", 4); + + // OLED_ShowString_AlignRight(AlignRightEND_P, 2, "192.168.0.69", 4); + // OLED_ShowString_AlignRight(37, 3, "1920*1080", 4); + OLED_ShowString_AlignRight(AlignRightEND_P, 3, " FPS", 4); + // OLED_ShowString(0, 3, "\"ABCDEFGHIJKLMMN\'", 4); + // OLED_ShowString(5, 2, "1", 4); +} + +void kvm_init_ui(void) +{ + if(kvm_hw_ver != 2){ + kvm_init_cube_ui(); + } else { + kvm_init_pcie_ui(); + } +} + +void qrcode_to_oled(QRCode *qr) +{ + char *p_oled_data; + uint16_t count = 0; + uint8_t bit; + uint8_t begin_x = 2; + uint8_t begin_y = 2; + p_oled_data = (char *)malloc( 132 * sizeof(char)); + if(p_oled_data != NULL){ + // fill + for(int i = 0; i < 132; i++){ + p_oled_data[i] = 0xFF; + } + // i | ; j - + for(int i = 0; i < 29; i++){ + for(int j = 0; j < 29; j++){ + if(qrIsBlacke(qr, i, j)){ + // 敲黑点 + uint16_t data_index = ((i+begin_y)/8)*33+(j+begin_x); + uint8_t mask = ~(0x01 << ((i+begin_y)%8)); + p_oled_data[data_index] &= mask; + } + } + } + OLED_Fill(); + OLED_ShowIMG(29, 0, p_oled_data, 33, 4); + free(p_oled_data); + } +} + +int qrencode(char *string) +{ + // test: qrencode("WIFI:T:WPA2;S:NanoKVM;P:12345678;;"); + int errcode = QR_ERR_NONE; + QRCode* p = qrInit(3, QR_EM_8BIT, 1, 4, &errcode); + if (p == NULL) { + printf("error\n"); + return -1; + } + qrAddData(p, (const qr_byte_t*)string, strlen(string)); + if (!qrFinalize(p)) { + printf("finalize error\n"); + return -1; + } + + qrcode_to_oled(p); + if(string[0] == 'W'){ + OLED_ShowStringTurn(3, 1, "WiFi", 8); + OLED_ShowStringTurn(3, 2, "AP:", 8); + } else if(string[0] == 'h'){ + if(string[7] == '1'){ + OLED_ShowStringTurn(3, 1, "Web:", 8); + } else if(string[8] == 'w'){ + OLED_ShowStringTurn(3, 1, "WiKi", 8); + } + } + int size = 0; + // width = height = qr_vertable[version] * mag + sep * mag * 2 + qr_byte_t * buffer = qrSymbolToBMP(p, 5, 5, &size); + if (buffer == NULL) { + printf("error %s", qrGetErrorInfo(p)); + return -1; + } + ofstream f("/etc/kvm/wifi_config.bmp"); + if (f.fail()) { + return -1; + } + f.write((const char *)buffer, size); + f.close(); + return 0; +} + +ip_addr_t show_which_ip(void) +{ + if(kvm_sys_state.wifi_state == -2) return ETH_IP; + static uint8_t run_count = 0; + static ip_addr_t ip_type = ETH_IP; + run_count++; + if(run_count > IP_Change_time/STATE_DELAY){ + run_count = 0; + switch(ip_type){ + case ETH_IP: + ip_type = WiFi_IP; + break; + case WiFi_IP: + ip_type = ETH_IP; + break; + default: + ip_type = ETH_IP; + } + } + // printf("show_which_ip? %d\n", ip_type); + return ip_type; +} + +uint8_t ip_changed(ip_addr_t ip_type) +{ + uint8_t *kvm_sys_ip; + uint8_t *kvm_oled_ip; + uint8_t ret; + if(ip_type == ETH_IP){ + kvm_sys_ip = kvm_sys_state.eth_addr; + kvm_oled_ip = kvm_oled_state.eth_addr; + } else if(ip_type == WiFi_IP){ + kvm_sys_ip = kvm_sys_state.wifi_addr; + kvm_oled_ip = kvm_oled_state.wifi_addr; + } else ret = 0; + for (int i = 0; i < 16; i++){ + if(kvm_sys_ip[i] == 0) ret = 0; + if(kvm_sys_ip[i] != kvm_oled_ip[i]) ret = 1; + } + if(ret == 1){ + memcpy(kvm_oled_ip, kvm_sys_ip, 16); + } + return ret; +} + +void kvm_eth_state_disp(ip_addr_t _ip_type, uint8_t first_disp) +{ + static ip_addr_t _ip_type_old = NULL_IP; + uint8_t temp; + // printf("[kvmd]eth_state = %d\n", kvm_sys_state.eth_state); + if( (kvm_oled_state.eth_state != kvm_sys_state.eth_state) || + (_ip_type_old != _ip_type) || + first_disp || ip_changed(ETH_IP)) + { + kvm_oled_state.eth_state = kvm_sys_state.eth_state; + _ip_type_old = _ip_type; + switch(kvm_oled_state.eth_state){ + case -1: + case 0: + temp = 0; + OLED_ShowKVMState(ETH_STATE, 0); + if(_ip_type == ETH_IP) + OLED_ShowKVMStreamState(KVM_ETH_IP, &temp); + break; + case 1: + OLED_ShowKVMState(ETH_STATE, 1); + if(_ip_type == ETH_IP) + OLED_ShowKVMStreamState(KVM_ETH_IP, &temp); + break; + case 2: + OLED_Show_Network_Error(1); + case 3: + OLED_Show_Network_Error(0); + OLED_ShowKVMState(ETH_STATE, 1); + if(_ip_type == ETH_IP) + OLED_ShowKVMStreamState(KVM_ETH_IP, kvm_sys_state.eth_addr); + break; + } + } +} + +void kvm_wifi_state_disp(ip_addr_t _ip_type, uint8_t first_disp) +{ + static ip_addr_t _ip_type_old = NULL_IP; + uint8_t temp; + // printf("[kvmd]wifi_state = %d\n", kvm_sys_state.wifi_state); + if( (kvm_oled_state.wifi_state != kvm_sys_state.wifi_state) || + (_ip_type_old != _ip_type) || + first_disp || ip_changed(WiFi_IP)) + { + kvm_oled_state.wifi_state = kvm_sys_state.wifi_state; + _ip_type_old = _ip_type; + switch(kvm_oled_state.wifi_state){ + case -2: + OLED_ShowKVMState(WIFI_STATE, -1); + break; + case -1: + case 0: + temp = 0; + OLED_ShowKVMState(WIFI_STATE, 0); + if(_ip_type == WiFi_IP) + OLED_ShowKVMStreamState(KVM_WIFI_IP, &temp); + break; + case 1: + OLED_ShowKVMState(WIFI_STATE, 1); + if(_ip_type == WiFi_IP){ + OLED_ShowKVMStreamState(KVM_WIFI_IP, kvm_sys_state.wifi_addr); + } + break; + } + } +} + +void kvm_usb_state_disp(uint8_t first_disp) +{ + if((kvm_oled_state.usb_state != kvm_sys_state.usb_state) || first_disp){ + kvm_oled_state.usb_state = kvm_sys_state.usb_state; + switch(kvm_oled_state.usb_state){ + case -1: + case 0: + OLED_ShowKVMState(HID_STATE, 0); + break; + case 1: + OLED_ShowKVMState(HID_STATE, 1); + break; + } + } +} + +void kvm_hdmi_state_disp(uint8_t first_disp) +{ + if((kvm_oled_state.hdmi_state != kvm_sys_state.hdmi_state) || first_disp){ + kvm_oled_state.hdmi_state = kvm_sys_state.hdmi_state; + switch(kvm_oled_state.hdmi_state){ + case -1: + case 0: + OLED_ShowKVMState(HDMI_STATE, 0); + break; + case 1: + OLED_ShowKVMState(HDMI_STATE, 1); + break; + } + } +} + +void kvm_fps_disp(uint8_t first_disp) +{ + if((kvm_oled_state.now_fps != kvm_sys_state.now_fps) || first_disp){ + kvm_oled_state.now_fps = kvm_sys_state.now_fps; + OLED_ShowKVMStreamState(KVM_STEAM_FPS, &kvm_oled_state.now_fps); + } +} + +void kvm_res_disp(uint8_t first_disp) +{ + if( (kvm_oled_state.hdmi_width != kvm_sys_state.hdmi_width) || \ + (kvm_oled_state.hdmi_height != kvm_sys_state.hdmi_height) || \ + first_disp ){ + kvm_oled_state.hdmi_width = kvm_sys_state.hdmi_width; + kvm_oled_state.hdmi_height = kvm_sys_state.hdmi_height; + OLED_Show_Res(kvm_sys_state.hdmi_width, kvm_sys_state.hdmi_height); + } +} + +void kvm_type_disp(uint8_t first_disp) +{ + if( (kvm_oled_state.type != kvm_sys_state.type) || first_disp){ + kvm_oled_state.type = kvm_sys_state.type; + OLED_ShowKVMStreamState(KVM_STEAM_TYPE, &kvm_oled_state.type); + } +} + +void kvm_qlty_disp(uint8_t first_disp) +{ + if( (kvm_oled_state.qlty != kvm_sys_state.qlty) || first_disp){ + kvm_oled_state.qlty = kvm_sys_state.qlty; + OLED_ShowKVMStreamState(KVM_JPG_QLTY, &kvm_sys_state.qlty); + } +} + +void kvm_main_disp(uint8_t first_disp) +{ + if(first_disp){ + OLED_Clear(); + kvm_init_ui(); + } +} + +void kvm_oled_clear(uint8_t subpage_changed) +{ + if(subpage_changed){ + OLED_Clear(); + } +} + +void kvm_main_ui_disp(uint8_t first_disp, uint8_t subpage_changed) +{ + ip_addr_t now_ip_type; + // if(kvm_oled_state.sub_page == 0) + // if(kvm_oled_state.oled_sleep_state == 1){ + if(kvm_oled_state.sub_page == 1){ + // main page (oled sleep) + kvm_oled_clear(first_disp || subpage_changed); + kvm_oled_state.oled_sleep_state = 1; + } else { + // main page + kvm_oled_state.oled_sleep_state = 0; + now_ip_type = show_which_ip(); + kvm_main_disp(first_disp || subpage_changed); + kvm_eth_state_disp(now_ip_type, first_disp || subpage_changed); + kvm_wifi_state_disp(now_ip_type, first_disp || subpage_changed); + kvm_usb_state_disp(first_disp || subpage_changed); + kvm_hdmi_state_disp(first_disp || subpage_changed); + kvm_fps_disp(first_disp || subpage_changed); + kvm_res_disp(first_disp || subpage_changed); + kvm_type_disp(first_disp || subpage_changed); + kvm_qlty_disp(first_disp || subpage_changed); + } +} + +uint8_t show_which_page() +{ + static uint8_t run_count = 0xfe; + static uint8_t show_type = 0; // 0 不动 1 QRcode; 2 text + if(sta_connect_ap()){ + if(ssid_pass_ok()){ + // + } + } + run_count++; + if(run_count > QR_Change_time/STATE_DELAY){ + run_count = 0; + if(show_type == 1) show_type = 2; + else show_type = 1; + return show_type; + } + return 0; +} + +void show_text_wifi_config(char *ap_ssid) +{ + OLED_Clear(); + OLED_ShowString(0, 0, "SSID:", 8); + OLED_ShowString_AlignRight(63, 1, "NanoKVM", 8); + OLED_ShowString(0, 2, "PASS:", 8); + OLED_ShowString_AlignRight(63, 3, ap_ssid, 8); +} + +void show_wifi_config_ip(void) +{ + OLED_Clear(); + get_ip_addr(WiFi_IP); + OLED_ShowString(0, 1, "Config IP:", 8); + OLED_ShowString_AlignRight(63, 2, (char*)kvm_sys_state.wifi_addr, 4); +} + +void show_wifi_config_QR(void) +{ + static char cmd[70]; + OLED_Clear(); + get_ip_addr(WiFi_IP); + printf("http://%s/#/wifi\n", kvm_sys_state.wifi_addr); + sprintf(cmd, "http://%s/#/wifi", kvm_sys_state.wifi_addr); + qrencode(cmd); +} + +void show_wifi_starting(void) +{ + OLED_Clear(); + OLED_ShowString(0, 1, "WiFi AP is", 8); + OLED_ShowString(0, 2, "Starting..", 8); +} + +void show_wifi_connecting(void) +{ + OLED_Clear(); + OLED_ShowString(0, 1, "WiFi", 8); + OLED_ShowString(0, 2, "Connect...", 8); +} + +void kvm_wifi_config_ui_disp(uint8_t first_disp, uint8_t subpage_changed) +{ + static char cmd[70]; + // printf("[kvmd]kvm_wifi_config_ui_disp %d | %d\n", first_disp, subpage_changed); + if(first_disp) kvm_start_wifi_config_process(); // 略有不妥,就这样吧 + if(first_disp || subpage_changed){ + switch(kvm_oled_state.sub_page){ + case 0: // wifi ap is starting + show_wifi_starting(); + break; + case 1: // QRcode + printf("WIFI:T:WPA2;S:NanoKVM;P:%s;;\n", kvm_sys_state.wifi_ap_pass); + sprintf(cmd, "WIFI:T:WPA2;S:NanoKVM;P:%s;;", kvm_sys_state.wifi_ap_pass); + qrencode(cmd); + break; + case 2: // Textcode + show_text_wifi_config(kvm_sys_state.wifi_ap_pass); + break; + case 3: // open IP with QR Code + show_wifi_config_QR(); + break; + case 4: // open IP + show_wifi_config_ip(); + break; + case 5: // Connecting... + show_wifi_connecting(); + break; + } + } + // switch(show_which_page()) +} + +void oled_auto_sleep_time_update(void) +{ + kvm_oled_state.oled_sleep_start = time::time_ms(); +} + +void oled_auto_sleep(void) +{ + uint8_t tmp8; + uint8_t sleep_close_signal = 0; + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + if(access("/etc/kvm/oled_sleep", F_OK) == 0){ + fp = fopen("/etc/kvm/oled_sleep", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + if(file_size != 0){ + tmp8 = atoi((char*)RW_Data); + } else { + tmp8 = OLED_SLEEP_DELAY_DEFAULT; + } + if(tmp8 != kvm_oled_state.oled_sleep_param){ + // printf("/etc/kvm/oled_sleep = %d\n", tmp8); + kvm_oled_state.oled_sleep_param = tmp8; + if(kvm_oled_state.oled_sleep_param < OLED_SLEEP_DELAY_MIN){ + sleep_close_signal = 1; + } else { + // printf("oled_auto_sleep_time_update\n"); + oled_auto_sleep_time_update(); + } + } + } else { + if(kvm_oled_state.oled_sleep_param != 0){ + kvm_oled_state.oled_sleep_param = 0; + sleep_close_signal = 1; + } + } + + if(kvm_oled_state.page == 0){ + if(kvm_oled_state.oled_sleep_param < OLED_SLEEP_DELAY_MIN){ + if(sleep_close_signal == 1){ + kvm_sys_state.sub_page = 0; + } + } else { + if((time::time_ms() - kvm_oled_state.oled_sleep_start)/1000 >= kvm_oled_state.oled_sleep_param){ + // kvm_oled_state.oled_sleep_state = 1; + kvm_sys_state.sub_page = 1; + } else { + kvm_sys_state.sub_page = 0; + } + } + } +} diff --git a/kvm_system/main/lib/oled_ui/oled_ui.h b/kvm_system/main/lib/oled_ui/oled_ui.h new file mode 100644 index 0000000..329873e --- /dev/null +++ b/kvm_system/main/lib/oled_ui/oled_ui.h @@ -0,0 +1,11 @@ +#ifndef OLED_UI_H_ +#define OLED_UI_H_ +#include "config.h" +#include "oled_ctrl.h" + +void kvm_main_ui_disp(uint8_t first_disp, uint8_t subpage_changed); +void kvm_wifi_config_ui_disp(uint8_t first_disp, uint8_t subpage_changed); +void oled_auto_sleep_time_update(void); +void oled_auto_sleep(void); + +#endif // OLED_UI_H_ \ No newline at end of file diff --git a/kvm_system/main/lib/system_ctrl/system_ctrl.cpp b/kvm_system/main/lib/system_ctrl/system_ctrl.cpp new file mode 100644 index 0000000..c367deb --- /dev/null +++ b/kvm_system/main/lib/system_ctrl/system_ctrl.cpp @@ -0,0 +1,181 @@ +#include "system_ctrl.h" + +using namespace maix; +using namespace maix::sys; + +extern kvm_sys_state_t kvm_sys_state; +extern kvm_oled_state_t kvm_oled_state; + +void gen_hostapd_conf(char* ap_ssid) +{ + char file_data[300] = {0}; + FILE * fp; + sprintf(file_data, "ctrl_interface=/var/run/hostapd\nctrl_interface_group=0\nssid=NanoKVM\nhw_mode=g\nchannel=1\nbeacon_int=100\ndtim_period=2\nmax_num_sta=255\nrts_threshold=-1\nfragm_threshold=-1\nmacaddr_acl=0\nauth_algs=3\nwpa=2\nwpa_passphrase=%s\nieee80211n=1\n", ap_ssid); + fp = fopen("/etc/hostapd.conf", "w"); + fwrite(file_data, sizeof(file_data) , 1, fp ); + fclose(fp); +} + +void gen_udhcpd_conf() +{ + char file_data[300] = {0}; + FILE * fp; + sprintf(file_data, "start 10.10.10.100\nend 10.10.10.200\ninterface wlan0\npidfile /var/run/udhcpd.wlan0.pid\nlease_file /var/lib/misc/udhcpd.wlan0.leases\noption subnet 255.255.255.0\noption lease 864000\n"); + fp = fopen("/etc/udhcpd.wlan0.conf", "w"); + fwrite(file_data, sizeof(file_data) , 1, fp ); + fclose(fp); +} + +void gen_dnsmasq_conf() +{ + char file_data[300] = {0}; + FILE * fp; + sprintf(file_data, "bind-interfaces\ninterface=wlan0\ndhcp-range=10.10.10.2,10.10.10.254\naddress=/#/10.0.0.1\n"); + fp = fopen("/etc/udhcpd.wlan0.conf", "w"); + fwrite(file_data, sizeof(file_data) , 1, fp ); + fclose(fp); +} + +uint8_t sta_connect_ap(void) +{ + uint8_t RW_Data[10]; + FILE *fp; + fp = popen("hostapd_cli all sta | grep aid", "r"); + fgets((char*)RW_Data, 2, fp); + pclose(fp); + if(RW_Data[0] == 'a') return 1; + else return 0; +} + +uint8_t ssid_pass_ok(void) +{ + if(access("/kvmapp/kvm/wifi_try_connect", F_OK) == 0) return 1; + else return 0; +} + +uint8_t wifi_connected(void) +{ + uint8_t RW_Data[20]; + FILE *fp; + fp = popen("wpa_cli -i wlan0 status | grep \"wpa_state\"", "r"); + fgets((char*)RW_Data, 15, fp); + pclose(fp); + if(RW_Data[10] == 'S') return 0; + else if(RW_Data[10] == 'C') return 1; + else return 0; +} + +void kvm_start_wifi_config_process(void) +{ + if(kvm_sys_state.wifi_config_process == -1){ + // printf("[kvmw]开始配置wifi\n"); + kvm_sys_state.wifi_config_process = 0; + kvm_sys_state.sub_page = 0; + } +} + +void kvm_wifi_web_config_process() +{ + if(ssid_pass_ok()){ + system("rm /kvmapp/kvm/wifi_try_connect"); + system("/etc/init.d/S30wifi restart"); + kvm_sys_state.wifi_config_process = 3; + // 尝试连接wifi + if(kvm_sys_state.wifi_config_process == 3){ + // time::sleep_ms(WIFI_CONNECTION_DELAY); + if(wifi_connected()){ + // 连上了 + kvm_sys_state.wifi_config_process = -1; + } else { + // 没连上 + kvm_sys_state.wifi_config_process = 0; + } + } + } +} + +void kvm_wifi_config_process() +{ + int temp; + char ran_num; + static char cmd[70]; + switch(kvm_sys_state.wifi_config_process){ + case -1: + // printf("[kvms]Not in the process of configuring WiFi\n"); + break; + case 0: // 启动wifi + srand((unsigned)time::time_ms()); + for(temp = 0; temp < 4; temp++){ + do{ + ran_num = (char)rand()%10 + '0'; + if(temp == 0) break; + } while (ran_num == kvm_sys_state.wifi_ap_pass[(temp-1)*2]); + kvm_sys_state.wifi_ap_pass[temp*2] = ran_num; + kvm_sys_state.wifi_ap_pass[temp*2+1] = ran_num; + } + kvm_sys_state.wifi_ap_pass[8] = '\0'; + + sprintf(cmd, "echo %s > /kvmapp/kvm/ap.pass", kvm_sys_state.wifi_ap_pass); + system("echo NanoKVM > /kvmapp/kvm/ap.ssid"); + system(cmd); + system("/etc/init.d/S30wifi ap"); + + kvm_sys_state.wifi_config_process = 1; + kvm_sys_state.sub_page = 1; + break; + case 1: // 等待设备连接 + if(sta_connect_ap()){ + kvm_sys_state.wifi_config_process = 2; + kvm_sys_state.sub_page = 3; + } + break; + case 2: // 等待输入帐号密码 + if(ssid_pass_ok()){ + system("rm /kvmapp/kvm/wifi_try_connect"); + system("/etc/init.d/S30wifi restart"); + kvm_sys_state.wifi_config_process = 3; + kvm_sys_state.sub_page = 5; + } + break; + case 3: // 尝试连接wifi + time::sleep_ms(WIFI_CONNECTION_DELAY); + if(wifi_connected()){ + // 连上了 + kvm_sys_state.wifi_config_process = -1; + kvm_sys_state.page = 0; + kvm_sys_state.sub_page = 0; + } else { + // 没连上 + kvm_sys_state.wifi_config_process = 0; + kvm_sys_state.sub_page = 0; + } + break; + } +} + +uint8_t kvm_reset_password(void) +{ + FILE *fp = popen("bash", "w"); + if (fp == NULL) { + perror("popen"); + return 0; + } + + fputs("passwd root\n", fp); + time::sleep_ms(10); + fputs("root\n", fp); + time::sleep_ms(10); + fputs("root\n", fp); + time::sleep_ms(10); + fputs("rm /etc/kvm/pwd\n", fp); + time::sleep_ms(10); + fputs("sync\n", fp); + time::sleep_ms(10); + fputs("exit\n", fp); // 退出 bash + + if (pclose(fp) == -1) { + perror("pclose"); + return 1; + } + return 0; +} \ No newline at end of file diff --git a/kvm_system/main/lib/system_ctrl/system_ctrl.h b/kvm_system/main/lib/system_ctrl/system_ctrl.h new file mode 100644 index 0000000..e16f246 --- /dev/null +++ b/kvm_system/main/lib/system_ctrl/system_ctrl.h @@ -0,0 +1,16 @@ +#ifndef SYSTEM_CTRL_H_ +#define SYSTEM_CTRL_H_ +#include "config.h" + +void gen_hostapd_conf(char* ap_ssid); +void gen_udhcpd_conf(); +void gen_dnsmasq_conf(); +uint8_t sta_connect_ap(void); +uint8_t ssid_pass_ok(void); +uint8_t wifi_connected(void); +void kvm_start_wifi_config_process(void); +void kvm_wifi_web_config_process(); +void kvm_wifi_config_process(); +uint8_t kvm_reset_password(void); + +#endif // SYSTEM_CTRL_H_ \ No newline at end of file diff --git a/kvm_system/main/lib/system_init/system_init.cpp b/kvm_system/main/lib/system_init/system_init.cpp new file mode 100644 index 0000000..f0c4cd3 --- /dev/null +++ b/kvm_system/main/lib/system_init/system_init.cpp @@ -0,0 +1,241 @@ +#include "config.h" +#include "system_init.h" + +using namespace maix; +using namespace maix::sys; + +extern kvm_sys_state_t kvm_sys_state; +extern kvm_oled_state_t kvm_oled_state; + +void new_app_init(uint8_t safe_update) +{ + // first start + system("cp -f /kvmapp/system/update-nanokvm.py /etc/kvm/"); + system("rm /etc/init.d/S02udisk"); + system("cp /kvmapp/system/init.d/S01fs /etc/init.d/"); + system("cp /kvmapp/system/init.d/S03usbdev /etc/init.d/"); + system("cp /kvmapp/system/init.d/S15kvmhwd /etc/init.d/"); + system("cp /kvmapp/system/init.d/S30eth /etc/init.d/"); + system("cp /kvmapp/system/init.d/S50sshd /etc/init.d/"); + if(kvm_wifi_exist()) system("cp /kvmapp/system/init.d/S30wifi /etc/init.d/"); + else system("rm /etc/init.d/S30wifi"); + system("cp /kvmapp/system/init.d/S98tailscaled /etc/init.d/"); + if(safe_update == 0){ + system("cp /kvmapp/system/init.d/S95nanokvm /etc/init.d/"); // /kvmapp/system/init.d/S95nanokvm还没跑完直接覆盖可能出问题 + } + + // system("rm /etc/init.d/S04backlight"); + // system("rm /etc/init.d/S05tp"); + // system("rm /etc/init.d/S40bluetoothd"); + // system("rm /etc/init.d/S99*"); + + system("mkdir /kvmapp/kvm"); + system("mkdir /etc/kvm"); + system("echo 0 > /kvmapp/kvm/now_fps"); + system("echo 30 > /kvmapp/kvm/fps"); + system("echo 2000 > /kvmapp/kvm/qlty"); + system("echo 720 > /kvmapp/kvm/res"); + system("echo h264 > /kvmapp/kvm/type"); + system("echo 0 > /kvmapp/kvm/state"); + system("touch /etc/kvm/frame_detact"); + + // rm jpg_stream & kvm_stream + system("rm -r /kvmapp/jpg_stream"); + system("rm /kvmapp/kvm_system/kvm_stream"); + + // rm kvm_new_app + system("rm /kvmapp/kvm_new_app"); + system("sync"); + // system("/etc/init.d/S95nanokvm restart"); + + // update ko + FILE *fp; + uint8_t RW_Data_0[30]; + uint8_t RW_Data_1[30]; + fp = popen("md5sum /mnt/system/ko/soph_mipi_rx.ko | grep 086ed01749188975afaa40fb569374f8 | awk '{print $2}'", "r"); + if ( NULL == fp ) + { + pclose(fp); + // return; + } + fgets((char*)RW_Data_0, 10, fp); + pclose(fp); + fp = popen("md5sum /mnt/system/ko/soph_mipi_rx.ko | grep 69be7eeded3777f750480a5dd5a1aa26 | awk '{print $2}'", "r"); + if ( NULL == fp ) + { + pclose(fp); + // return; + } + fgets((char*)RW_Data_1, 10, fp); + pclose(fp); + + int8_t hdmi_ver = -1; + + if(access("/etc/kvm/hdmi_version", F_OK) == 0){ + uint8_t RW_Data[2]; + FILE *fp = fopen("/etc/kvm/hdmi_version", "r"); + fread(RW_Data, sizeof(char), 2, fp); + fclose(fp); + if(RW_Data[0] == 'c') hdmi_ver = 1; + else if(RW_Data[0] == 'u') hdmi_ver = 2; + } + + if(hdmi_ver == 2){ + if(RW_Data_1[0] != '/'){ + system("cp /kvmapp/system/ko/soph_mipi_rx.ko /mnt/system/ko/soph_mipi_rx.ko"); + system("sync"); + system("reboot"); + } else { + system("sync"); + system("/etc/init.d/S15kvmhwd start"); + system("/etc/init.d/S95nanokvm restart"); + } + } else { + if((RW_Data_0[0] != '/') && (RW_Data_1[0] != '/')){ + system("cp /kvmapp/system/ko/soph_mipi_rx.ko /mnt/system/ko/soph_mipi_rx.ko"); + system("sync"); + system("reboot"); + } else { + system("sync"); + system("/etc/init.d/S15kvmhwd start"); + system("/etc/init.d/S95nanokvm restart"); + } + } + + if(access("/root/old/kvm_new_img", F_OK) == 0){ + system("rm -r /root/old"); + } +} + +void update_resolv_conf(void) +{ + FILE *fp = NULL; + // fp = fopen("/boot/resolv.conf", "w+"); + fp = fopen("/boot/resolv.conf", "w"); + // 阿里: 223.5.5.5 + // 腾讯: 119.29.29.29 + fprintf(fp, "nameserver 192.168.0.1\nnameserver 8.8.4.4\nnameserver 8.8.8.8\nnameserver 114.114.114.114\nnameserver 119.29.29.29\nnameserver 223.5.5.5"); + fclose(fp); + system("rm -rf /etc/resolv.conf"); + system("cp -vf /etc/resolv.conf /etc/resolv.conf.old"); + system("cp -vf /boot/resolv.conf /etc/resolv.conf"); +} + +void init_upadte(void) +{ + if(access("/kvmapp/kvm_new_app", F_OK) == 0){ + new_app_init(0); + } + + // PCIe Patch + if(access("/kvmapp/jpg_stream/dl_lib/libmaixcam_lib.so", F_OK) == 0){ + system("cp -f /kvmapp/jpg_stream/dl_lib/libmaixcam_lib.so /kvmapp/kvm_system/dl_lib/"); + new_app_init(1); + } + + // DNS Patch + system("tailscale set --accept-dns=false"); + if(access("/boot/resolv.conf", F_OK) != 0){ + // 不存在直接覆盖 + update_resolv_conf(); + } else { + // 存在则检测是否拷贝/DNS是否正确 + uint8_t RW_Data[10] = {0}; + FILE *fp; + fp = popen("cat /etc/resolv.conf | grep 119.29.29.29", "r"); + fgets((char*)RW_Data, 2, fp); + pclose(fp); + if(RW_Data[0] != 'n') { + update_resolv_conf(); + } + } + + // libmaixcam_lib.so + if(access("/kvmapp/kvm_system/dl_lib/libmaixcam_lib.so", F_OK) == 0){ + // exist + system("cp -f /kvmapp/kvm_system/dl_lib/libmaixcam_lib.so /kvmapp/server/dl_lib"); + } else { + printf("kvm_system not exist lib!\n"); + } + system("sync"); + system("killall NanoKVM-Server"); + system("rm -r /tmp/server"); + system("cp -r /kvmapp/server /tmp/"); + system("/tmp/server/NanoKVM-Server &"); +} + +void first_start(void) +{ + if(access("/kvmapp/kvm_new_img", F_OK) == 0){ + // init oled + OLED_state = oled_exist(); + if(OLED_state){ + printf("oled exist\r\n"); + } else { + printf("oled not exist\r\n"); + } + if(OLED_state){ + OLED_Init(); + OLED_ColorTurn(0); //0正常显示 1 反色显示 + OLED_DisplayTurn(0); //0正常显示 1 屏幕翻转显示 + // OLED_INIT_UI(); + + if(kvm_hw_ver == 2){ + OLED_Revolve(); + } + } + uint8_t update_begin = 0; + while(1){ + if(chack_net_state(ETH_ROUTE) && get_ip_addr(ETH_IP)){ + OLED_Clear(); + if(kvm_hw_ver != 2){ + if(update_begin == 0){ + update_begin = 1; + OLED_ShowString(1, 2, "Auto-updating...", 16); + OLED_ShowStringtoend(1, 4, (char*)kvm_sys_state.eth_addr, 16, 0); + system("python /kvmapp/system/update-nanokvm.py"); + } else { + OLED_ShowString(1, 0, "Auto-update fail", 16); + OLED_ShowString(1, 2, "Please open the ", 16); + OLED_ShowStringtoend(1, 4, (char*)kvm_sys_state.eth_addr, 16, 0); + OLED_ShowString(1, 6, "update KVM APP ", 16); + } + } else { + // PCIe + if(update_begin == 0){ + update_begin = 1; + OLED_ShowString(0, 1, "Updating...", 8); + OLED_ShowString_AlignRight(AlignRightEND_P, 2, (char*)kvm_sys_state.eth_addr, 4); + system("python /kvmapp/system/update-nanokvm.py"); + } else { + OLED_ShowString(0, 0, "Update fail", 8); + OLED_ShowString(0, 1, "Please open", 8); + OLED_ShowString_AlignRight(AlignRightEND_P, 2, (char*)kvm_sys_state.eth_addr, 4); + OLED_ShowString(0, 3, "update APP" , 8); + } + } + while(1){ + time::sleep_ms(1000); + if(chack_net_state(ETH_ROUTE) == 0) { + OLED_Clear(); + break; + } + } + } else { + get_ip_addr(ETH_ROUTE); + if(kvm_hw_ver != 2){ + OLED_ShowString(1, 2, " Please connect ", 16); + OLED_ShowString(1, 4, " to the network ", 16); + OLED_ShowString(1, 6, " to update app ", 16); + } else { + // PCIe + OLED_ShowString(0, 0, "Please link", 8); + OLED_ShowString(0, 1, "to network", 8); + OLED_ShowString(0, 2, "update APP" , 8); + } + + time::sleep_ms(10); + } + } + } +} \ No newline at end of file diff --git a/kvm_system/main/lib/system_init/system_init.h b/kvm_system/main/lib/system_init/system_init.h new file mode 100644 index 0000000..b0a9e7c --- /dev/null +++ b/kvm_system/main/lib/system_init/system_init.h @@ -0,0 +1,10 @@ +#ifndef SYSTEM_INIT_H_ +#define SYSTEM_INIT_H_ +#include "config.h" + +void new_app_init(uint8_t safe_update); +void update_resolv_conf(void); +void init_upadte(void); +void first_start(void); + +#endif // SYSTEM_INIT_H_ diff --git a/kvm_system/main/lib/system_state/system_state.cpp b/kvm_system/main/lib/system_state/system_state.cpp new file mode 100644 index 0000000..be0461f --- /dev/null +++ b/kvm_system/main/lib/system_state/system_state.cpp @@ -0,0 +1,544 @@ +#include "config.h" +#include "system_state.h" + +using namespace maix; +using namespace maix::sys; + +extern kvm_sys_state_t kvm_sys_state; +extern kvm_oled_state_t kvm_oled_state; + +// net_port +int get_ip_addr(ip_addr_t ip_type) +{ + switch (ip_type){ + case ETH_IP: // eth_addr + if(strcmp(ip_address()["eth0"].c_str(), (char*)kvm_sys_state.eth_addr) != 0){ + if(*(ip_address()["eth0"].c_str()) == NULL){ + printf("can`t get ip addr\r\n"); + kvm_sys_state.eth_addr[0] = 0; + return 0; + } + for(int i = 0; i <= 15; i++) + { + kvm_sys_state.eth_addr[i] = *(ip_address()["eth0"].c_str() + i); + printf("%c", kvm_sys_state.eth_addr[i]); + } + printf("\r\n"); + } + return 1; + case WiFi_IP: // wifi_addr + if(strcmp(ip_address()["wlan0"].c_str(), (char*)kvm_sys_state.wifi_addr) != 0){ + if(*(ip_address()["wlan0"].c_str()) == NULL){ + printf("can`t get ip addr\r\n"); + kvm_sys_state.wifi_addr[0] = 0; + return 0; + } + for(int i = 0; i <= 15; i++) + { + kvm_sys_state.wifi_addr[i] = *(ip_address()["wlan0"].c_str() + i); + printf("%c", kvm_sys_state.wifi_addr[i]); + } + printf("\r\n"); + } + return 1; + case Tailscale_IP: // tail_addr + if(*(ip_address()["tailscale0"].c_str()) == NULL){ + printf("can`t get ip addr\r\n"); + kvm_sys_state.tail_addr[0] = 0; + return 0; + } + for(int i = 0; i <= 15; i++) + { + kvm_sys_state.tail_addr[i] = *(ip_address()["tailscale0"].c_str() + i); + printf("%c", kvm_sys_state.tail_addr[i]); + } + printf("\r\n"); + return 1; + case RNDIS_IP: // rndis_addr + if(*(ip_address()["usb0"].c_str()) == NULL){ + printf("can`t get ip addr\r\n"); + kvm_sys_state.rndis_addr[0] = 0; + return 0; + } + for(int i = 0; i <= 15; i++) + { + kvm_sys_state.rndis_addr[i] = *(ip_address()["usb0"].c_str() + i); + printf("%c", kvm_sys_state.rndis_addr[i]); + } + printf("\r\n"); + return 1; + case ETH_ROUTE: // eth_route + if(access("/etc/kvm/gateway", F_OK) != 0){ + // 不存在gateway文件 + memset( kvm_sys_state.eth_route, 0, sizeof( kvm_sys_state.eth_route ) ); + char Cmd[100]={0}; + memset( Cmd, 0, sizeof( Cmd ) ); + sprintf( Cmd,"ip route | grep -i '^default' | grep -i 'eth0' | awk '{print $3}'"); + FILE* fp = popen( Cmd, "r" ); + if ( NULL == fp ) + { + pclose(fp); + return 0; + } + memset( kvm_sys_state.eth_route, 0, sizeof( kvm_sys_state.eth_route ) ); + while ( NULL != fgets( (char*)kvm_sys_state.eth_route,sizeof( kvm_sys_state.eth_route ),fp )) + { + // printf("ip=%s\n",kvm_sys_state.eth_route); + break; + } + if(kvm_sys_state.eth_route[0] == 0){ + // 开机时未插入ETH + pclose(fp); + return 0; + } + for(int i = 0; i < 40; i++){ + if(kvm_sys_state.eth_route[i] == 10){ + kvm_sys_state.eth_route[i] = ' '; + break; + } + } + pclose(fp); + return 1; + } else { + int file_size; + FILE *fp = fopen("/etc/kvm/gateway", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(kvm_sys_state.eth_route, sizeof(char), file_size, fp); + fclose(fp); + return 1; + } + case WiFi_ROUTE: // wifi_route + memset( kvm_sys_state.wifi_route, 0, sizeof( kvm_sys_state.wifi_route ) ); + char Cmd[100]={0}; + memset( Cmd, 0, sizeof( Cmd ) ); + sprintf( Cmd,"ip route | grep -i '^default' | grep -i 'wlan0' | awk '{print $3}'"); + FILE* fp = popen( Cmd, "r" ); + if ( NULL == fp ) + { + pclose(fp); + return 0; + } + memset( kvm_sys_state.wifi_route, 0, sizeof( kvm_sys_state.wifi_route ) ); + while ( NULL != fgets( (char*)kvm_sys_state.wifi_route,sizeof( kvm_sys_state.wifi_route ),fp )) + { + // printf("ip=%s\n",kvm_sys_state.wifi_route); + break; + } + if(kvm_sys_state.wifi_route[0] == 0){ + // 开机时未插入ETH + pclose(fp); + return 0; + } + for(int i = 0; i < 40; i++){ + if(kvm_sys_state.wifi_route[i] == 10){ + kvm_sys_state.wifi_route[i] = ' '; + break; + } + } + pclose(fp); + return 1; + } + return 0; +} + +int chack_net_state(ip_addr_t use_ip_type) +{ + char Cmd[100]={0}; + if (use_ip_type == ETH_ROUTE) sprintf( Cmd,"ping -I eth0 -w 1 %s > /dev/null", kvm_sys_state.eth_route); + else if (use_ip_type == WiFi_ROUTE) sprintf( Cmd,"ping -I wlan0 -w 1 %s > /dev/null", kvm_sys_state.wifi_route); + else return -1; // 不支持的端口 + if(system(Cmd) == 0){ // 256:不通; = 0:通 + return 1; + } + return 0; +} + +void patch_eth_wifi(void) +{ + // system("ip link set eth0 down"); + // system("ip link set eth0 up"); + // system("udhcpc -i eth0 &"); +} + +int kvm_eth_cable_exist() +{ + int temp; + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + fp = fopen("/sys/class/net/eth0/carrier", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + if(RW_Data[0] == '0') return 0; + else if(RW_Data[0] == '1') return 1; + return -1; +} + +int kvm_wifi_exist() +{ + // if(access("/sys/bus/sdio/devices/mmc1*", F_OK) == 0) return 1; + // else return 0; + uint8_t RW_Data[10]; + FILE *fp; + fp = popen("ifconfig | grep wlan", "r"); + fgets((char*)RW_Data, 2, fp); + pclose(fp); + if(RW_Data[0] == 'w') return 1; + else return 0; +} + +int kvm_rndis_exist() +{ + // if(access("/sys/kernel/config/usb_gadget/g0/configs/c.1/rndis.usb0", F_OK) == 0) return 1; + // else return 0; + + int temp; + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + fp = fopen("/sys/class/net/usb0/carrier", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + if(RW_Data[0] == '0') return 0; + else if(RW_Data[0] == '1') return 1; + return -1; +} + +int kvm_tailscale_exist() +{ + // tailscale status + + int temp; + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + fp = fopen("ifconfig tailscale0 | grep 'inet addr' | awk '{print $2}'", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + if(RW_Data[0] == 'a') return 1; + else return 0; + return -1; +} + +void kvm_update_usb_state() +{ + // usb_state, hid_state, rndis_state, udisk_state + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + fp = fopen("/sys/class/udc/4340000.usb/state", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + if(RW_Data[0] == 'n') kvm_sys_state.usb_state = 0; + else if(RW_Data[0] == 'c') kvm_sys_state.usb_state = 1; + else kvm_sys_state.usb_state = -1; + // hid_state & udisk_state (rndis_state单独处理) + if(kvm_sys_state.usb_state == 1){ + if(access("/sys/kernel/config/usb_gadget/g0/configs/c.1/hid.GS*", F_OK) == 0) + kvm_sys_state.hid_state = 1; + if(access("/sys/kernel/config/usb_gadget/g0/configs/c.1/mass_storage.disk0", F_OK) == 0) + kvm_sys_state.udisk_state = 1; + } else { + kvm_sys_state.hid_state = 0; + kvm_sys_state.udisk_state = 0; + } +} + +void kvm_update_hdmi_state() +{ + static uint8_t check_times = 4; + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + if(++check_times > 5){ + check_times = 0; + fp = popen("cat /proc/cvitek/vi_dbg | grep VIFPS | awk '{print $3}'", "r"); + if (fp == NULL) { + pclose(fp); + return; + } + fgets((char*)RW_Data, 2, fp); + pclose(fp); + // printf("[kvmd]HDMI exist? %c\n", RW_Data[0]); + if (RW_Data[0] != '0'){ + kvm_sys_state.hdmi_state = 1; + } else { + kvm_sys_state.hdmi_state = 0; + } + } +} + +void kvm_update_stream_fps(void) +{ + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + + // FPS + fp = fopen("/kvmapp/kvm/now_fps", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + RW_Data[file_size] = 0; + kvm_sys_state.now_fps = atoi((char*)RW_Data); +} + +void kvm_update_stream_type(void) +{ + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + + // type + fp = fopen("/kvmapp/kvm/type", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + if(RW_Data[0] == 'm') kvm_sys_state.type = KVM_TYPE_MJPG; + else if(RW_Data[0] == 'h') kvm_sys_state.type = KVM_TYPE_H264; + else kvm_sys_state.type = KVM_TYPE_none; +} + +void kvm_update_stream_qlty(void) +{ + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + uint16_t tmp16; + + // QLTY + fp = fopen("/kvmapp/kvm/qlty", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + RW_Data[file_size] = 0; + tmp16 = atoi((char*)RW_Data); + if(kvm_sys_state.type == KVM_TYPE_MJPG){ + if(tmp16 < 60) kvm_sys_state.qlty = 1; + else if(tmp16 >= 60 && tmp16 < 75) kvm_sys_state.qlty = 2; + else if(tmp16 >= 75 && tmp16 < 90) kvm_sys_state.qlty = 3; + else if(tmp16 >= 90 && tmp16 <= 100) kvm_sys_state.qlty = 4; + else kvm_sys_state.qlty = 4; + } else { + if(tmp16 < 1500) kvm_sys_state.qlty = 1; + else if(tmp16 >= 1500 && tmp16 < 2500) kvm_sys_state.qlty = 2; + else if(tmp16 >= 2500 && tmp16 < 3500) kvm_sys_state.qlty = 3; + else if(tmp16 >= 3500 && tmp16 <= 5000) kvm_sys_state.qlty = 4; + else kvm_sys_state.qlty = 4; + } +} + +void kvm_update_hdmi_res(void) +{ + FILE *fp; + int file_size; + uint8_t RW_Data[10]; + // HDMI width + fp = fopen("/kvmapp/kvm/width", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + RW_Data[file_size] = 0; + kvm_sys_state.hdmi_width = atoi((char*)RW_Data); + // HDMI height + fp = fopen("/kvmapp/kvm/height", "r"); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + fread(RW_Data, sizeof(char), file_size, fp); + fclose(fp); + RW_Data[file_size] = 0; + kvm_sys_state.hdmi_height = atoi((char*)RW_Data); +} + +void kvm_update_eth_state(void) +{ + // 嗨得是老架构啊 + static uint8_t eth_cable_state = 0; + // if(kvm_eth_cable_exist() == 1){ + // if(eth_cable_state == 0){ + // system("udhcpc -i eth0 &"); + // } + // eth_cable_state = 1; + // } else { + // eth_cable_state = 0; + // } + eth_cable_state = kvm_eth_cable_exist(); + + if(eth_cable_state){ + // 获取实时IP + if(strcmp(ip_address()["eth0"].c_str(), (char*)kvm_sys_state.eth_addr) != 0){ + // if(1){ + if(get_ip_addr(ETH_IP)){ + kvm_sys_state.eth_state = 2; + } else { + kvm_sys_state.eth_state = 1; + return; + } + // OLED_ShowKVMStreamState(KVM_IP, kvm_sys_state.ip_addr); + } + // ping 网关 + if(kvm_sys_state.eth_route[0] == 0){ + get_ip_addr(ETH_ROUTE); + } else { + if(chack_net_state(ETH_ROUTE)){ + // 网络通 + kvm_sys_state.eth_state = 3; + } else { + kvm_sys_state.eth_state = 2; + } + } + + } else { + kvm_sys_state.eth_state = 0; + patch_eth_wifi(); + } + + /* + switch (kvm_sys_state.eth_state){ + case -1: + // 初始缺省值 + kvm_sys_state.eth_state = 0; + case 0: + // 存在PHY + printf("kvm_update_eth_state case 0\n"); + if (kvm_eth_cable_exist()) { + // 已插入 + // system("udhcpc -i eth0 > /dev/null &"); + kvm_sys_state.eth_state = 1; + } else break; + // break; // 消除短暂感叹号 + case 1: + // 线缆已插入&无网络 + if (get_ip_addr(ETH_IP)){ + kvm_sys_state.eth_state = 2; + } else break; + case 2: + // 已获取ip + // system("udhcpc -i eth0 > /dev/null"); + if (get_ip_addr(ETH_ROUTE) && chack_net_state(ETH_ROUTE)){ + // ping 通 + kvm_sys_state.eth_state = 3; + } + if (kvm_eth_cable_exist() == 0) { + // 未插入 + kvm_sys_state.eth_state = 0; + patch_eth_wifi(); + } + break; + case 3: + // 有网络&检测是否拔出/持续检测是否能ping通 + if (kvm_sys_state.eth_route[0] != 0){ + if (chack_net_state(ETH_ROUTE) == 0){ + // ping不通 + kvm_sys_state.eth_state = 2; + // patch_eth_wifi(); + } + } + if (kvm_eth_cable_exist() == 0) { + // 未插入 + kvm_sys_state.eth_state = 0; + patch_eth_wifi(); + } + break; + default: + kvm_sys_state.eth_state = 0; + } + */ +} + +void kvm_update_wifi_state(void) +{ + // 无wifi模块(检测存在?)->有模块&未联网(检测是否联网)-> + if(kvm_sys_state.wifi_state == -2) return; + switch (kvm_sys_state.wifi_state){ + case -1: + // 初始缺省值 + if (kvm_wifi_exist()) { + kvm_sys_state.wifi_state = 0; + system("touch /etc/kvm/wifi_exist"); + } + else { + kvm_sys_state.wifi_state = -2; // 不存在wifi模块,直接跳出 + system("rm /etc/kvm/wifi_exist"); + return; + } + // break; // 直接开始检测联网 + case 0: + // 存在WiFi&未联网 + system("echo 0 > /kvmapp/kvm/wifi_state"); + if (get_ip_addr(WiFi_IP) && get_ip_addr(WiFi_ROUTE)){ + // 已获取ip+route + if (chack_net_state(WiFi_ROUTE)){ + // ping 通 + kvm_sys_state.wifi_state = 1; + } + } + break; + case 1: + // 已联网&持续检测是否能ping通 + system("echo 1 > /kvmapp/kvm/wifi_state"); + get_ip_addr(WiFi_IP); + if (kvm_sys_state.wifi_route[0] != 0){ + if (chack_net_state(WiFi_ROUTE) == 0){ + // ping 通 + kvm_sys_state.wifi_state = 0; + } + } + // default: + // kvm_sys_state.wifi_state = -1; + } +} + +void kvm_update_rndis_state(void) +{ + if (kvm_rndis_exist()) { + if(kvm_sys_state.rndis_state != 1){ + if (get_ip_addr(RNDIS_IP)) { + kvm_sys_state.rndis_state = 1; + } + } + } + else kvm_sys_state.rndis_state = 0; +} + +void kvm_update_tailscale_state(void) +{ + if (kvm_tailscale_exist()) { + if(kvm_sys_state.tail_state != 1){ + if (get_ip_addr(Tailscale_IP)) { + kvm_sys_state.tail_state = 1; + } + } + } + else kvm_sys_state.tail_state = 0; +} + +//============================================================================ + +uint8_t ion_free_space(void) +{ + //cat /sys/kernel/debug/ion/cvi_carveout_heap_dump/summary | grep "usage rate:" | awk '{print $2}' +} \ No newline at end of file diff --git a/kvm_system/main/lib/system_state/system_state.h b/kvm_system/main/lib/system_state/system_state.h new file mode 100644 index 0000000..93ae2c7 --- /dev/null +++ b/kvm_system/main/lib/system_state/system_state.h @@ -0,0 +1,30 @@ +#ifndef SYSTEM_STATE_H_ +#define SYSTEM_STATE_H_ +#include "config.h" + +enum ip_addr_t +{ + ETH_IP=1, WiFi_IP, Tailscale_IP, RNDIS_IP, ETH_ROUTE, WiFi_ROUTE, NULL_IP +}; + +// net_port +int get_ip_addr(ip_addr_t ip_type); +int chack_net_state(ip_addr_t use_ip_type); +void patch_eth_wifi(void); +int kvm_eth_cable_exist(); +int kvm_wifi_exist(); +int kvm_rndis_exist(); +int kvm_tailscale_exist(); +void kvm_update_usb_state(); +void kvm_update_hdmi_state(); +void kvm_update_stream_fps(void); +void kvm_update_stream_type(void); +void kvm_update_stream_qlty(void); +void kvm_update_hdmi_res(void); +void kvm_update_eth_state(void); +void kvm_update_wifi_state(void); +void kvm_update_rndis_state(void); +void kvm_update_tailscale_state(void); +uint8_t ion_free_space(void); + +#endif // SYSTEM_STATE_H_ diff --git a/kvm_system/main/src/main.cpp b/kvm_system/main/src/main.cpp new file mode 100644 index 0000000..2476842 --- /dev/null +++ b/kvm_system/main/src/main.cpp @@ -0,0 +1,233 @@ +#include "config.h" + +using namespace maix; +using namespace maix::sys; +using namespace maix::peripheral; + +kvm_sys_state_t kvm_sys_state; +kvm_oled_state_t kvm_oled_state; + +/* +init.d: +/etc/init.d/S00kmod +/etc/init.d/S01fs + +*/ + +void build_recovery(void) +{ + uint8_t need_recovery = 0; + if (access("/recovery", F_OK) != 0) { + printf("unexist /recovery\n"); + mkdir("/recovery", 0777); + need_recovery = 1; + } else { + printf("exist /recovery\n"); + + } +} + +void* thread_oled_handle(void * arg) +{ + OLED_Init(); + OLED_ColorTurn(0); //0正常显示 1 反色显示 + OLED_DisplayTurn(0); //0正常显示 1 屏幕翻转显示 + OLED_Clear(); + + while(kvm_sys_state.oled_thread_running) + { + oled_auto_sleep(); + // printf("[kvmd]thread_oled_handle - while\n"); + uint8_t page_changed = (kvm_oled_state.page == kvm_sys_state.page)? 0:1; + uint8_t subpage_changed = (kvm_oled_state.sub_page == kvm_sys_state.sub_page)? 0:1; + // printf("subpage_changed = %d", subpage_changed); + // printf("kvm_oled_state.sub_page = %d", kvm_oled_state.sub_page); + // printf("kvm_sys_state.sub_page = %d\n", kvm_sys_state.sub_page); + kvm_oled_state.page = kvm_sys_state.page; + kvm_oled_state.sub_page = kvm_sys_state.sub_page; + + + switch(kvm_oled_state.page){ + case 0 : // main page + kvm_main_ui_disp(page_changed, subpage_changed); + break; + case 1 : // wifi config page + kvm_wifi_config_ui_disp(page_changed, subpage_changed); + break; + default: + OLED_Clear(); + } + time::sleep_ms(OLED_DELAY); + } + OLED_Clear(); + kvm_sys_state.oled_thread_running = -1; +} + +void* thread_key_handle(void * arg) +{ + uint64_t __attribute__((unused)) press_time; + uint32_t press_cycle; + int fd = open ("/dev/input/event0", O_RDONLY); + if (fd == -1) { + kvm_sys_state.key_thread_running = 0; + } + struct input_event event; + + while(kvm_sys_state.key_thread_running) + { + read (fd, &event, sizeof (event)); + if (event.type == EV_KEY) { + if (event.value == 1){ + // printf ("[kvmk]按键按下\n"); + press_time = time::time_ms(); + } else if (event.value == 0){ + oled_auto_sleep_time_update(); + // printf ("[kvmk]按键抬起\n"); + press_cycle = time::time_ms() - press_time; + if(press_cycle >= KEY_LONGLONG_PRESS){ + kvm_reset_password(); + } else if (press_cycle >= KEY_LONG_PRESS && press_cycle < KEY_LONGLONG_PRESS){ + // long + // printf ("[kvmk]按键长按\n"); + // printf("[kvmk]wifi_state = %d\n", kvm_sys_state.wifi_state); + if(kvm_sys_state.wifi_state == -2){ + kvm_sys_state.page = 0; + kvm_sys_state.sub_page = 0; + continue; + } + switch(kvm_sys_state.page){ + case 0: // main page + kvm_sys_state.page = 1; + kvm_sys_state.sub_page = 0; + break; + case 1: // wifi coonfig page + system("/etc/init.d/S30wifi restart"); + kvm_sys_state.page = 0; + kvm_sys_state.sub_page = 0; + kvm_sys_state.wifi_config_process = -1; + break; + } + } else { + // short + // printf ("[kvmk]按键短按\n"); + // printf ("[kvmk]wifi_config_process = %d\n", kvm_sys_state.wifi_config_process); + // printf ("[kvmk]page = %d\n", kvm_sys_state.page); + // printf ("[kvmk]sub_page = %d\n", kvm_sys_state.sub_page); + switch(kvm_sys_state.page){ + case 0: // main page + if(kvm_sys_state.sub_page == 0) kvm_sys_state.sub_page = 1; + else kvm_sys_state.sub_page = 0; + break; + case 1: // wifi coonfig page + switch(kvm_sys_state.wifi_config_process){ + case 1: // QR1<->TEXT2 + if(kvm_sys_state.sub_page == 1) kvm_sys_state.sub_page = 2; + else kvm_sys_state.sub_page = 1; + // printf("[kvmk]sub_page = %d\n", kvm_sys_state.sub_page); + break; + case 2: + if(kvm_sys_state.sub_page == 1) kvm_sys_state.sub_page = 2; + else if(kvm_sys_state.sub_page == 2) kvm_sys_state.sub_page = 3; + else if(kvm_sys_state.sub_page == 3) kvm_sys_state.sub_page = 4; + else kvm_sys_state.sub_page = 1; + break; + } + break; + } + } + } + } + time::sleep_ms(KEY_DELAY); + } + kvm_sys_state.key_thread_running = 0; +} + +void* thread_sys_handle(void * arg) +{ + while(kvm_sys_state.sys_thread_running) + { + // printf("[kvmsys]main while start!\n"); + // net + if(kvm_sys_state.page == 0){ + kvm_update_eth_state(); + kvm_update_wifi_state(); + // kvm_update_rndis_state(); + // kvm_update_tailscale_state(); + // sys_state + kvm_update_usb_state(); + kvm_update_hdmi_state(); + kvm_update_stream_fps(); + kvm_update_hdmi_res(); + kvm_update_stream_type(); + kvm_update_stream_qlty(); + + kvm_wifi_web_config_process(); + } else if(kvm_sys_state.page == 1){ + kvm_wifi_config_process(); + } + + time::sleep_ms(STATE_DELAY); + } + kvm_sys_state.sys_thread_running = 0; +} + +int main(int argc, char* argv[]) +{ + // Catch SIGINT signal(e.g. Ctrl + C), and set exit flag to true. + signal(SIGINT, [](int sig){ + kvm_sys_state.oled_thread_running = 0; + kvm_sys_state.key_thread_running = 0; + kvm_sys_state.sys_thread_running = 0; + app::set_exit_flag(true); + log::info("[kvms]Prepare to exit\n"); + }); + + pthread_t sys_state_thread; + pthread_t display_thread; + pthread_t key_thread; + + // first_start(); + + init_upadte(); + + OLED_state = oled_exist(); + if(OLED_state){ + printf("oled exist\r\n"); + system("touch /etc/kvm/oled_exist"); + } else { + printf("oled not exist\r\n"); + system("rm /etc/kvm/oled_exist"); + } + + if(kvm_sys_state.sys_thread_running == 0){ + kvm_sys_state.sys_thread_running = 1; + if (0 != pthread_create(&sys_state_thread, NULL, thread_sys_handle, NULL)) { + kvm_sys_state.sys_thread_running = 0; + printf("[kvms]create thread failed!\r\n"); + } + } + if(OLED_state == 1 && kvm_sys_state.oled_thread_running == 0){ + kvm_sys_state.oled_thread_running = 1; + if (0 != pthread_create(&display_thread, NULL, thread_oled_handle, NULL)) { + kvm_sys_state.oled_thread_running = 0; + printf("[kvms]create thread failed!\r\n"); + } + } + if(kvm_sys_state.key_thread_running == 0){ + kvm_sys_state.key_thread_running = 1; + if (0 != pthread_create(&key_thread, NULL, thread_key_handle, NULL)) { + kvm_sys_state.key_thread_running = 0; + printf("[kvms]create thread failed!\r\n"); + } + } + + // while(!app::need_exit()){ + while(kvm_sys_state.sys_thread_running){ + time::sleep_ms(1000); + } + kvm_sys_state.sys_thread_running = 0; + kvm_sys_state.oled_thread_running = 0; + kvm_sys_state.key_thread_running = 0; + while(kvm_sys_state.oled_thread_running != -1) time::sleep_ms(100); +} + diff --git a/kvmapp/.DS_Store b/kvmapp/.DS_Store new file mode 100755 index 0000000000000000000000000000000000000000..418f549573247ee4d94b3c85918d884d15cf2152 GIT binary patch literal 6148 zcmeHK&2G~`5S~p#x^acb0f}Ca_QEYf2~^M?kW46t-YOZv0Z?iat2H>@D0XVKA;@>0 zpnVCRgD2?Yz_&j{P2EbI5P}_P_M4sAZykRdd%Z-&2D4G0s6#{?uCUQYbB(FG@|rbl zrUhhrj+_dbQcNL@YolG_Z&X07okt&OLWeY@OY66n$`4U0Bh=NO<2n4{4{eBuHs})_ zW0i-LVZBRWh&d)#Sl_O&yd^8A6MeOq#;Y85|93c1nNRGm`yNfwBrkfs3)Wa`-fFFL z?s4z7_dYn1Wl)60EFXl^x2m@v7DthdUrCuR<1l)i#O1Ja_l3-gFv;SnCMTm9L*Bhf zvXLwYa+Zy9Jx^c?oVR&<*x8)VpZ2!8qQAYp=!*Hyqh42RJ$|xSwE4XU{bzdzHO_@fH^1laD2X#fBK literal 0 HcmV?d00001 diff --git a/kvmapp/jpg_stream/S95nanokvm b/kvmapp/jpg_stream/S95nanokvm new file mode 100755 index 0000000..056508f --- /dev/null +++ b/kvmapp/jpg_stream/S95nanokvm @@ -0,0 +1,91 @@ +#!/bin/sh +# nanokvm Rev2.1 + +case "$1" in + start) + echo -n kvm > /boot/hostname.prefix + cp /mnt/data/sensor_cfg.ini.LT /mnt/data/sensor_cfg.ini + + str_value=$(cat /sys/class/cvi-base/base_uid | awk '{print $2}') + first_uint=$(echo $str_value | cut -d'_' -f1) + second_uint=$(echo $str_value | cut -d'_' -f2) + result="$first_uint$second_uint" + echo $result > /device_key + + # if [ ! -e /etc/kvm/hw ] + # then + + # fi + + # echo 504 > /sys/class/gpio/export # pwr led + # echo 505 > /sys/class/gpio/export # hdd led + # echo 503 > /sys/class/gpio/export # pwr key + # echo 507 > /sys/class/gpio/export # rst key + + # echo in > /sys/class/gpio/gpio504/direction # pwr led + # echo in > /sys/class/gpio/gpio505/direction # hdd led + # echo out > /sys/class/gpio/gpio503/direction # pwr key + # echo out > /sys/class/gpio/gpio507/direction # rst key + + iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT + iptables -A OUTPUT -o eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT + iptables -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT + iptables -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT + iptables -A OUTPUT -o eth0 -p tcp --sport 8000 -m state --state ESTABLISHED -j DROP + + cp -r /kvmapp/kvm_system /tmp/ + /tmp/kvm_system/kvm_system & + + # if [ -e /kvmapp/kvm_stream ] + # then + # cp -r /kvmapp/kvm_stream /tmp/ + # /tmp/kvm_stream/kvm_stream & + # fi + + cp -r /kvmapp/server /tmp/ + /tmp/server/NanoKVM-Server & + ;; + stop) + killall kvm_system + # killall kvm_stream + killall NanoKVM-Server + rm -r /tmp/kvm_system + # rm -r /tmp/kvm_stream + rm -r /tmp/server + echo "OK" + ;; + # restart_stream) + # killall kvm_stream + # rm -r /tmp/kvm_stream + # if [ -e /kvmapp/kvm_stream ] + # then + # cp -r /kvmapp/kvm_stream /tmp/ + # /tmp/kvm_stream/kvm_stream & + # echo "OK" + # fi + # ;; + restart) + killall kvm_system + # killall kvm_stream + killall NanoKVM-Server + rm -r /tmp/kvm_system + # rm -r /tmp/kvm_stream + rm -r /tmp/server + + cp -r /kvmapp/kvm_system /tmp/ + /tmp/kvm_system/kvm_system & + + # if [ -e /kvmapp/kvm_stream ] + # then + # cp -r /kvmapp/kvm_stream /tmp/ + # /tmp/kvm_stream/kvm_stream & + # fi + + cp -r /kvmapp/server /tmp/ + /tmp/server/NanoKVM-Server & + + sync + + echo "OK" + ;; +esac \ No newline at end of file diff --git a/kvmapp/jpg_stream/jpg_stream b/kvmapp/jpg_stream/jpg_stream new file mode 100755 index 0000000..1a0030f --- /dev/null +++ b/kvmapp/jpg_stream/jpg_stream @@ -0,0 +1,5 @@ +rm /etc/init.d/S95webkvm +cp /kvmapp/jpg_stream/S95nanokvm /etc/init.d/ +cp /kvmapp/jpg_stream/dl_lib/libmaixcam_lib.so /kvmapp/kvm_system/dl_lib +rm -r /kvmapp/jpg_stream +/etc/init.d/S95nanokvm restart diff --git a/kvmapp/kvm_new_app b/kvmapp/kvm_new_app new file mode 100644 index 0000000..e69de29 diff --git a/kvmapp/kvm_system/kvm_stream b/kvmapp/kvm_system/kvm_stream new file mode 100755 index 0000000..e69de29 diff --git a/kvmapp/kvm_system/kvm_system b/kvmapp/kvm_system/kvm_system new file mode 100755 index 0000000000000000000000000000000000000000..f2c63d07022374e5fa616ac55db6357775e15a58 GIT binary patch literal 325048 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 CiI$-N literal 0 HcmV?d00001 diff --git a/kvmapp/system/init.d/S01fs b/kvmapp/system/init.d/S01fs new file mode 100755 index 0000000..3391d66 --- /dev/null +++ b/kvmapp/system/init.d/S01fs @@ -0,0 +1,42 @@ +#!/bin/sh + +if [ "$1" = "start" ] +then + # use all sdcard free space for data + parted -s /dev/mmcblk0 "resizepart 2 -0" + echo "yes + 8192MB + " | parted ---pretend-input-tty /dev/mmcblk0 "resizepart 2 8192MB" + # resize data filesystem + (resize2fs /dev/mmcblk0p2) & + + + . /etc/profile + printf "mounting filesystem : " + mkdir -p /boot + mount -t vfat /dev/mmcblk0p1 /boot + mount -t configfs configfs /sys/kernel/config + mount -t debugfs debugfs /sys/kernel/debug + + if [ -e /boot/usb.disk0 ] + then + if [ ! -e /etc/kvm.disk0 ] + then + touch /etc/kvm.disk0 + # use all sdcard free space for data + parted -s /dev/mmcblk0 "mkpart primary 8193MB 100%" + sleep 1 + # resize data filesystem + (mkfs.exfat /dev/mmcblk0p3) & + sleep 1 + fi + fi + + if [ -e /dev/mmcblk0p3 ] + then + mkdir -p /data + mount /dev/mmcblk0p3 /data + fi + + echo "OK" +fi diff --git a/kvmapp/system/init.d/S03usbdev b/kvmapp/system/init.d/S03usbdev new file mode 100755 index 0000000..1cd5ba8 --- /dev/null +++ b/kvmapp/system/init.d/S03usbdev @@ -0,0 +1,128 @@ +#!/bin/sh +if [ "$1" = "start" ] +then + . /etc/profile + echo "usb mode: device" + cd /sys/kernel/config/usb_gadget + + mkdir g0 + cd g0 + # echo 0x3346 > idVendor + # echo 0x1009 > idProduct + # mkdir strings/0x409 + # echo '0123456789ABCDEF' > strings/0x409/serialnumber + # echo 'flyingrtx' > strings/0x409/manufacturer + # echo 'licheervnano' > strings/0x409/product + + echo 0x3346 > idVendor + echo 0x1009 > idProduct + mkdir strings/0x409 + echo '0123456789ABCDEF' > strings/0x409/serialnumber + echo 'sipeed' > strings/0x409/manufacturer + echo 'NanoKVM' > strings/0x409/product + + mkdir configs/c.1 + echo 0xE0 > configs/c.1/bmAttributes + echo 120 > configs/c.1/MaxPower + mkdir configs/c.1/strings/0x409 + echo "NanoKVM" > configs/c.1/strings/0x409/configuration + + if [ -e /boot/usb.ncm ] + then + mkdir functions/ncm.usb0 + ln -s functions/ncm.usb0 configs/c.1/ + else + if [ -e /boot/usb.rndis0 ] + then + mkdir functions/rndis.usb0 + ln -s functions/rndis.usb0 configs/c.1/ + fi + fi + + # keyboard + mkdir functions/hid.GS0 + if [ -e /boot/BIOS ] + then + echo 1 > functions/hid.GS0/subclass + fi + if [ ! -e /boot/usb.notwakeup ] + then + echo 1 > functions/hid.GS0/wakeup_on_write + fi + echo 1 > functions/hid.GS0/protocol + echo 6 > functions/hid.GS0/report_length +# echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x10\\x75\\x01\\x95\\x08\\x01\\x02\\x95\\x01\\x75\\x08\\x01\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.GS0/report_desc + echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.GS0/report_desc + ln -s functions/hid.GS0 configs/c.1 + + # mouse + mkdir functions/hid.GS1 + if [ -e /boot/BIOS ] + then + echo 1 > functions/hid.GS1/subclass + fi + if [ ! -e /boot/usb.notwakeup ] + then + echo 1 > functions/hid.GS1/wakeup_on_write + fi + echo 2 > functions/hid.GS1/protocol + echo -ne \\x34 > functions/hid.GS1/report_length + echo -ne \\x5\\x1\\x9\\x2\\xa1\\x1\\x9\\x1\\xa1\\x0\\x5\\x9\\x19\\x1\\x29\\x3\\x15\\x0\\x25\\x1\\x95\\x3\\x75\\x1\\x81\\x2\\x95\\x1\\x75\\x5\\x81\\x3\\x5\\x1\\x9\\x30\\x9\\x31\\x9\\x38\\x15\\x81\\x25\\x7f\\x75\\x8\\x95\\x3\\x81\\x6\\xc0\\xc0 > functions/hid.GS1/report_desc + ln -s functions/hid.GS1 configs/c.1 + + # touchpad + mkdir functions/hid.GS2 + if [ -e /boot/BIOS ] + then + echo 1 > functions/hid.GS2/subclass + fi + if [ ! -e /boot/usb.notwakeup ] + then + echo 1 > functions/hid.GS2/wakeup_on_write + fi + echo 2 > functions/hid.GS2/protocol + echo 6 > functions/hid.GS2/report_length + echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x01\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x00\\x26\\xff\\x7f\\x35\\x00\\x46\\xff\\x7f\\x75\\x10\\x95\\x02\\x81\\x02\\x05\\x01\\x09\\x38\\x15\\x81\\x25\\x7f\\x35\\x00\\x45\\x00\\x75\\x08\\x95\\x01\\x81\\x06\\xc0\\xc0 > functions/hid.GS2/report_desc + ln -s functions/hid.GS2 configs/c.1 + + if [ -e /boot/usb.disk0 ] + then + mkdir functions/mass_storage.disk0 + ln -s functions/mass_storage.disk0 configs/c.1/ + echo 1 > functions/mass_storage.disk0/lun.0/removable + if [ -e /boot/usb.disk0.ro ] + then + echo 1 > functions/mass_storage.disk0/lun.0/ro + echo 0 > functions/mass_storage.disk0/lun.0/cdrom + fi + disk=$(cat /boot/usb.disk0) + if [ -z "${disk}" ] + then + # if [ ! -e /mnt/usbdisk.img ] + # then + # fallocate -l 8G /mnt/usbdisk.img + # mkfs.vfat /mnt/usbdisk.img + # fi + echo /dev/mmcblk0p3 > functions/mass_storage.disk0/lun.0/file + else + cat /boot/usb.disk0 > functions/mass_storage.disk0/lun.0/file + fi + fi + + ls /sys/class/udc/ | cat > UDC + echo device > /proc/cviusb/otg_role +fi + +if [ "$1" = "stop" ] +then + echo '' > /sys/kernel/config/usb_gadget/g0/UDC + echo host > /proc/cviusb/otg_role +fi + +if [ "$1" = "restart" ] +then + echo > /sys/kernel/config/usb_gadget/g0/UDC + sleep 1 + ls /sys/class/udc/ | cat > /sys/kernel/config/usb_gadget/g0/UDC + echo "USB Restart OK!" +fi diff --git a/kvmapp/system/init.d/S15kvmhwd b/kvmapp/system/init.d/S15kvmhwd new file mode 100755 index 0000000..0b414e6 --- /dev/null +++ b/kvmapp/system/init.d/S15kvmhwd @@ -0,0 +1,209 @@ +#!/bin/sh +# kvmhwd Rev2.3 + +Alpha_OLED_RST_Pin=371 +Beta_OLED_RST_Pin=502 +Beta_OLED_SCL=595 +Beta_OLED_SDA=507 +WiFi_EN_Pin=506 +Alpha_PWR_LED_Pin=504 +Alpha_HDD_LED_Pin=505 +Alpha_PWR_KEY_Pin=503 +Alpha_RST_KEY_Pin=507 +Beta_PWR_LED_Pin=504 +Beta_PWR_KEY_Pin=503 +Beta_RST_KEY_Pin=505 +PCIe_HDMI_RST_Pin=451 + +init_alpha_hw(){ + devmem 0x030010D0 32 0x2 # I2C1_SCL + devmem 0x030010DC 32 0x2 # I2C1_SDA + devmem 0x030010D4 32 0x3 # OLED_RST + devmem 0x0300103C 32 0x3 # GPIOA15 + devmem 0x03001050 32 0x3 # GPIOA22 + devmem 0x0300105C 32 0x3 # GPIOA23 + devmem 0x03001060 32 0x3 # GPIOA24 + devmem 0x03001054 32 0x3 # GPIOA25 + devmem 0x03001058 32 0x3 # GPIOA27 + + devmem 0x03001068 32 0x6 # GPIOA 18 UART1 RX + devmem 0x03001064 32 0x6 # GPIOA 19 UART1 TX + devmem 0x03001070 32 0x2 # GPIOA 28 UART2 TX + devmem 0x03001074 32 0x2 # GPIOA 29 UART2 RX + + echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/export # OLED_RST + echo out > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/direction + echo 1 > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/value + + echo ${Alpha_PWR_LED_Pin} > /sys/class/gpio/export # pwr led + echo ${Alpha_HDD_LED_Pin} > /sys/class/gpio/export # hdd led + echo ${Alpha_PWR_KEY_Pin} > /sys/class/gpio/export # pwr key + echo ${Alpha_RST_KEY_Pin} > /sys/class/gpio/export # rst key + + echo in > /sys/class/gpio/gpio${Alpha_PWR_LED_Pin}/direction # pwr led + echo in > /sys/class/gpio/gpio${Alpha_HDD_LED_Pin}/direction # hdd led + echo out > /sys/class/gpio/gpio${Alpha_PWR_KEY_Pin}/direction # pwr key + echo out > /sys/class/gpio/gpio${Alpha_RST_KEY_Pin}/direction # rst key + + rmmod /mnt/system/ko/i2c-gpio.ko + rmmod /mnt/system/ko/i2c-algo-bit.ko + + # rm /etc/init.d/S25wifimod + # rm /etc/init.d/S30wifi +} + +init_beta_pcie_hw(){ + devmem 0x0300103C 32 0x3 # GPIOA15 + devmem 0x03001050 32 0x3 # GPIOA22 + devmem 0x0300105C 32 0x3 # GPIOA23 + devmem 0x03001060 32 0x3 # GPIOA24 + devmem 0x03001054 32 0x3 # GPIOA25 + devmem 0x03001058 32 0x3 # GPIOA27 + + devmem 0x030010E4 32 0x0 # SDIO CLK + devmem 0x030010E0 32 0x0 # SDIO CMD + devmem 0x030010DC 32 0x0 # SDIO D0 + devmem 0x030010D8 32 0x0 # SDIO D1 + devmem 0x030010D4 32 0x0 # SDIO D2 + devmem 0x030010D0 32 0x0 # SDIO D3 + + devmem 0x03001068 32 0x6 # GPIOA 18 UART1 RX + devmem 0x03001064 32 0x6 # GPIOA 19 UART1 TX + devmem 0x03001070 32 0x2 # GPIOA 28 UART2 TX + devmem 0x03001074 32 0x2 # GPIOA 29 UART2 RX + + echo ${Beta_OLED_RST_Pin} > /sys/class/gpio/export # Beta OLED_RST + echo out > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/direction + echo 1 > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/value + + echo ${Beta_PWR_LED_Pin} > /sys/class/gpio/export # pwr led + echo ${Beta_PWR_KEY_Pin} > /sys/class/gpio/export # pwr key + echo ${Beta_RST_KEY_Pin} > /sys/class/gpio/export # rst key + echo ${PCIe_HDMI_RST_Pin} > /sys/class/gpio/export # hdmi rst key + + echo in > /sys/class/gpio/gpio${Beta_PWR_LED_Pin}/direction # pwr led + echo out > /sys/class/gpio/gpio${Beta_PWR_KEY_Pin}/direction # pwr key + echo out > /sys/class/gpio/gpio${Beta_RST_KEY_Pin}/direction # rst key + echo out > /sys/class/gpio/gpio${PCIe_HDMI_RST_Pin}/direction # rst key + + echo 1 > /sys/class/gpio/gpio${PCIe_HDMI_RST_Pin}/value # rst key + + rmmod /mnt/system/ko/i2c-gpio.ko + rmmod /mnt/system/ko/i2c-algo-bit.ko + insmod /mnt/system/ko/i2c-algo-bit.ko + insmod /mnt/system/ko/i2c-gpio.ko +} + +kvm_hw_detect(){ + if [ ! -e /etc/kvm/hdmi_version ] + then + rm /etc/kvm/hw + fi + if [ -e /etc/kvm/hw ] + then + echo "/etc/kvm/hw exist" + else + echo "/etc/kvm/hw not exist" + if [ -d "/etc/kvm/" ] + then + echo "/etc/kvm/ exist" + else + mkdir /etc/kvm/ + fi + devmem 0x0300104C 32 0x3 # GPIOA26 / WiFi_EN + echo ${WiFi_EN_Pin} > /sys/class/gpio/export # WiFi_EN + echo out > /sys/class/gpio/gpio${WiFi_EN_Pin}/direction + echo 0 > /sys/class/gpio/gpio${WiFi_EN_Pin}/value + + devmem 0x0300103C 32 0x3 # GPIOA15 + devmem 0x03001050 32 0x3 # GPIOA22 + devmem 0x0300105C 32 0x3 # GPIOA23 + devmem 0x03001060 32 0x3 # GPIOA24 + devmem 0x03001054 32 0x3 # GPIOA25 + devmem 0x03001058 32 0x3 # GPIOA27 + + devmem 0x030010D0 32 0x2 # I2C1_SCL + devmem 0x030010DC 32 0x2 # I2C1_SDA + devmem 0x030010D4 32 0x3 # OLED_RST + echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/export # OLED_RST + echo out > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/direction + echo 1 > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/value + + kvm_tmp=$(i2cdetect -ry 1 0x3d 0x3d | grep 3d) + if [ -n "$kvm_tmp" ] + then + # alpha hw + echo "alpha" > /etc/kvm/hw + else + # beta/pcie hw + echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/unexport # OLED_RST + echo ${Beta_OLED_RST_Pin} > /sys/class/gpio/export # Beta_OLED_RST_Pin + echo out > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/direction + echo 1 > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/value + + rmmod /mnt/system/ko/i2c-gpio.ko + rmmod /mnt/system/ko/i2c-algo-bit.ko + insmod /mnt/system/ko/i2c-algo-bit.ko + insmod /mnt/system/ko/i2c-gpio.ko + + kvm_tmp=$(i2cdetect -ry 4 0x2c 0x2c | grep 2c) + if [ -n "$kvm_tmp" ] + then + echo "c" > /etc/kvm/hdmi_version + else + echo "u" > /etc/kvm/hdmi_version + fi + + kvm_tmp=$(i2cdetect -ry 5 0x3c 0x3c | grep 3c) + if [ -n "$kvm_tmp" ] + then + echo "pcie" > /etc/kvm/hw + sync + reboot + else + echo "beta" > /etc/kvm/hw + # rm /etc/init.d/S25wifimod + # rm /etc/init.d/S30wifi + sync + fi + + echo 1 > /sys/class/gpio/gpio${WiFi_EN_Pin}/value + echo ${WiFi_EN_Pin} > /sys/class/gpio/unexport # WiFi_EN + fi + fi + sync +} + +kvm_hw_init(){ + FIND_FILE="/etc/kvm/hw" + if [ `grep -c "alpha" $FIND_FILE` -ne '0' ] + then + echo "hw = alpha!" + init_alpha_hw + fi + if [ `grep -c "beta" $FIND_FILE` -ne '0' ] + then + echo "hw = beta!" + init_beta_pcie_hw + fi + if [ `grep -c "pcie" $FIND_FILE` -ne '0' ] + then + echo "hw = pcie!" + init_beta_pcie_hw + fi +} + +case "$1" in + start) + kvm_hw_detect + kvm_hw_init + ;; + re-detect) + rm /etc/kvm/hw + kvm_hw_detect + kvm_hw_init + ;; + re-init) + kvm_hw_init + ;; +esac diff --git a/kvmapp/system/init.d/S30eth b/kvmapp/system/init.d/S30eth new file mode 100755 index 0000000..92928e4 --- /dev/null +++ b/kvmapp/system/init.d/S30eth @@ -0,0 +1,115 @@ +#!/bin/sh + +. /etc/profile + +# /boot/eth.nodhcp for example +# ipaddr/net gw[optional] +# 192.168.0.101/24 192.168.0.1 +# 192.168.3.116/22 + +RESERVE_INET="192.168.0.1/24" + +start() { + printf "start ethernet: " + if [ -e /boot/eth.nodhcp ] + then + [ -e /boot/eth.nodhcp ] && + cat /boot/eth.nodhcp | while read inet gw + do + addr=${inet%/*} + netid=${inet#*/} + [ -z $gw ] && + gw=$( echo $addr| ( IFS='.' read a b c d; echo $(( + (((((($a<<8)+$b)<<8)+$c)<<8)+$d) + & (((1<<$netid)-1)<<(32-$netid)) + )) + )) && + gw=$(($gw>>24&0xff)).$(($gw>>16&0xff)).$(($gw>>8&0xff)).$((1+( $gw>>0&0xff ))) + + arping -Dqc2 -Ieth0 $addr || continue + ip a add $inet brd + dev eth0 + ip r add default via $gw dev eth0 + cat > /etc/resolve.conf << EOF +nameserver $gw +nameserver 8.8.8.8 +nameserver 114.114.114.114 +EOF + break + done && + ip a show dev eth0|grep inet || ( + udhcpc -i eth0 -t 3 -T 1 -A 5 -b -p /run/udhcpc.eth0.pid &>/dev/null + ip a show dev eth0|grep inet + ) || ( + # failed to apply dynamic addr, need a available static addr to visit the LAN + inet=$RESERVE_INET + addr=${inet%/*} + ip a add $inet brd + dev eth0 + ) || exit 1 + else + (udhcpc -i eth0 -t 10 -T 1 -A 5 -b -p /run/udhcpc.eth0.pid) & + fi + + echo "OK" +} +stop() { + [[ ! -e "/run/udhcpc.eth0.pid" ]] && echo "udhcpc is not running..." && exit 1 + kill `cat /run/udhcpc.eth0.pid` + rm /run/udhcpc.eth0.pid +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|reload) + $0 stop + $0 start + ;; + doc) + cat < /etc/wpa_supplicant.conf + wpa_passphrase "$ssid" "$pass" >> /etc/wpa_supplicant.conf + wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf + if [ ! -e /boot/wifi.nodhcp ] + then + (udhcpc -i wlan0 -t 10 -T 1 -A 5 -b -p /run/udhcpc.wlan0.pid) & + fi +} + +ap_start() { + echo "wifi mode: ap" + id2=$(printf "%d" 0x$(sha512sum /sys/class/cvi-base/base_uid | head -c 2)) + id3=$(printf "%d" 0x$(sha512sum /sys/class/cvi-base/base_uid | head -c 4 | tail -c 2)) + if [ "$id2" = "$id3" ] + then + id2=$((id2 + 1)) + fi + if [ "$id2" -ge 255 ] + then + id2=253 + fi + if [ "$id3" -ge 255 ] + then + id3=254 + fi + ssid="" + pass="" + if [ -e /kvmapp/kvm/ap.ssid ] + then + ssid=`cat /kvmapp/kvm/ap.ssid` + fi + if [ -e /kvmapp/kvm/ap.pass ] + then + pass=`cat /kvmapp/kvm/ap.pass` + fi + gen_hostapd_conf "$ssid" "$pass" > /etc/hostapd.conf + ipv4_prefix=10.$id3.$id2 + if [ ! -e /etc/udhcpd.wlan0.conf ] + then + gen_udhcpd_conf wlan0 "${ipv4_prefix}" > /etc/udhcpd.wlan0.conf + fi + # gen_dnsmasq_conf "${ipv4_prefix}" > /etc/dnsmasq.conf + + # iptables --policy INPUT ACCEPT + # iptables --policy FORWARD ACCEPT + # iptables --policy OUTPUT ACCEPT + # iptables -F + # iptables -t nat -F + # iptables -t nat -A PREROUTING -i wlan0 -p udp --dport 53 -j DNAT --to ${ipv4_prefix}.1 + + ifconfig wlan0 up + ip route del default || true + ip add flush dev wlan0 + ip addr add $ipv4_prefix.1/24 dev wlan0 + # dnsmasq --pid-file=/tmp/dnsmasq.run -C /etc/dnsmasq.conf + hostapd -B -i wlan0 /etc/hostapd.conf + udhcpd -S /etc/udhcpd.wlan0.conf +} + +stop() { + ps -ef|grep hostapd|grep -v grep|awk '{print $1}'|xargs kill -2 || true + ps -ef|grep "udhcpd -S /etc/udhcpd.wlan0.conf" |grep -v grep|awk '{print $1}'|xargs kill -2 || true + killall wpa_supplicant || true + if [ -e /run/udhcpc.wlan0.pid ] + then + kill `cat /run/udhcpc.wlan0.pid` || true + rm -f /run/udhcpc.wlan0.pid + fi + if [ -e /var/run/udhcpd.wlan0.pid ] + then + kill `cat /var/run/udhcpd.wlan0.pid` || true + rm -f /var/run/udhcpd.wlan0.pid + fi + if [ -e /tmp/dnsmasq.run ] + then + kill `cat /tmp/dnsmasq.run` || true + rm -f /tmp/dnsmasq.run + fi + # if [ -e /etc/wpa_supplicant.conf] + # then + # echo > /etc/wpa_supplicant.conf + # fi + airmon-ng stop wlan0mon || true +} + +restart() { + stop + start +} + +if [ "${1}" = "start" ] +then + start +elif [ "${1}" = "stop" ] +then + stop +elif [ "${1}" = "ap" ] +then + stop + ap_start +elif [ "${1}" = "restart" ] +then + restart +fi diff --git a/kvmapp/system/init.d/S50sshd b/kvmapp/system/init.d/S50sshd new file mode 100755 index 0000000..911ed91 --- /dev/null +++ b/kvmapp/system/init.d/S50sshd @@ -0,0 +1,61 @@ +#!/bin/sh +# +# sshd Starts sshd. +# + +# Make sure the ssh-keygen progam exists +[ -f /usr/bin/ssh-keygen ] || exit 0 + +umask 077 + +startssh() { + /usr/bin/ssh-keygen -A + + printf "Starting sshd: " + /usr/sbin/sshd + touch /var/lock/sshd + echo "OK" +} +start() { + # Create any missing keys + if [ -e /etc/kvm/ssh_stop ] + then + if [ -e /boot/start_ssh_once ] + then + rm /boot/start_ssh_once + startssh + else + echo "SSH does not start" + fi + else + startssh + fi +} +stop() { + printf "Stopping sshd: " + killall sshd + rm -f /var/lock/sshd + echo "OK" +} +restart() { + stop + start +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|reload) + restart + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 +esac + +exit $? + diff --git a/kvmapp/system/init.d/S95nanokvm b/kvmapp/system/init.d/S95nanokvm new file mode 100755 index 0000000..b028421 --- /dev/null +++ b/kvmapp/system/init.d/S95nanokvm @@ -0,0 +1,50 @@ +#!/bin/sh +# nanokvm Rev3.0 + +case "$1" in + start) + echo -n kvm > /boot/hostname.prefix + cp /mnt/data/sensor_cfg.ini.LT /mnt/data/sensor_cfg.ini + + str_value=$(cat /sys/class/cvi-base/base_uid | awk '{print $2}') + first_uint=$(echo $str_value | cut -d'_' -f1) + second_uint=$(echo $str_value | cut -d'_' -f2) + result="$first_uint$second_uint" + echo $result > /device_key + + iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT + iptables -A OUTPUT -o eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT + iptables -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT + iptables -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT + iptables -A OUTPUT -o eth0 -p tcp --sport 8000 -m state --state ESTABLISHED -j DROP + + cp -r /kvmapp/kvm_system /tmp/ + /tmp/kvm_system/kvm_system & + + # cp -r /kvmapp/server /tmp/ + # /tmp/server/NanoKVM-Server & + ;; + stop) + killall kvm_system + killall NanoKVM-Server + rm -r /tmp/kvm_system + rm -r /tmp/server + echo "OK" + ;; + restart) + killall kvm_system + killall NanoKVM-Server + rm -r /tmp/kvm_system + rm -r /tmp/server + + cp -r /kvmapp/kvm_system /tmp/ + /tmp/kvm_system/kvm_system & + + # cp -r /kvmapp/server /tmp/ + # /tmp/server/NanoKVM-Server & + + sync + + echo "OK" + ;; +esac \ No newline at end of file diff --git a/kvmapp/system/init.d/S98tailscaled b/kvmapp/system/init.d/S98tailscaled new file mode 100755 index 0000000..d9a683e --- /dev/null +++ b/kvmapp/system/init.d/S98tailscaled @@ -0,0 +1,113 @@ +#!/bin/sh + +DAEMON="tailscaled" +PIDFILE="/var/run/$DAEMON.pid" + +# Set the port to listen on for incoming VPN packets. +# Remote nodes will automatically be informed about the new port number, +# but you might want to configure this in order to set external firewall +# settings. +PORT="41641" + +# Extra flags you might want to pass to tailscaled. +FLAGS="" + +# You need tailscaled at /usr/sbin to server, and tailscale at /usr/bin to operate +# STATIC version needed. Download page at https://pkgs.tailscale.com/stable/#static +PKG_URL_LATEST="https://pkgs.tailscale.com/stable/tailscale_latest_riscv64.tgz" +[ ! -x /usr/sbin/$DAEMON ] && + echo "/usr/sbin/$DAEMON not found, please download it from $PKG_URL_LATEST" && + echo "Then unpack it, copy $DAEMON to /usr/sbin and copy tailscale to /usr/bin" && + exit 1 +VERSION=$(/usr/sbin/$DAEMON --version|sed -n '1p'|xargs echo -n) + +[ -x /usr/bin/tailscale ] || echo "/usr/bin/tailscale not found, your installation of tailscale may be broken" + +# just for those need forwarding +[ ! -f /etc/sysctl.d/99-tailscale.conf ] && + mkdir -p /etc/sysctl.d/ && + echo "missing /etc/sysctl.d/99-tailscale.conf, try make it below:" && + (tee /etc/sysctl.d/99-tailscale.conf </dev/null + [ $? = 0 ] && echo "OK" || echo "FAIL" + ;; + restart|reload) + $0 stop + $0 start + ;; + doc) + cat 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

  • yMFa`T;e$y8Q%dh`kPF@-ONJ#qhj=> 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