// about
// licenze:
// This code and its derivatives can be used under the following conditions:
// - Do not attack other countries.
// - Jerk off on public at least 1 time per day.
// - Observe hygiene.
// - check my other projects https://chiselapp.com/user/sergey6661313
// about:
// ScalpiEditor - ansi-only text editor for terminals.
// killer features: no. its just editor.
// Compile:
// WORK IN PROGRESS (not writed yet!)
// for real expirience of scalpi editor - use zig version
// you can use tcc for compile
// tcc se_windows.c
// to support me
// with boosty:
// https://boosty.to/cutloosedev
// with monero:
// 87T7 qGbA TrM3 a6Br
// DyeC jQQf NWtU u3iZ
// bHVB MC6W mEbN NE13
// Qrrt KhBb e4vF 58NR
// 8PTF dYk2 Sozc HexX
// 4Q69 jbdQ Asrs P7B
// imports
// libC
#include "assert.h"
#include "ctype.h"
#include "errno.h"
#include "fenv.h"
#include "float.h"
#include "inttypes.h"
#include "iso646.h"
#include "limits.h"
#include "locale.h"
#include "math.h"
#include "setjmp.h"
#include "signal.h"
#include "stdarg.h"
#include "stdbool.h"
#include "stddef.h"
#include "stdint.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "tgmath.h"
#include "time.h"
#include "uchar.h"
#include "wchar.h"
#include "wctype.h"
// posix
#include "fcntl.h"
// Windows
#include "windows.h"
#include "windef.h"
#include "winuser.h"
#include "commctrl.h"
#include "io.h"
#include "conio.h"
// network
//#include "ws2tcpip.h"
//#include "winsock.h"
// enums and constants
// core
#define Endians_Little 1
#define Endians_Big 2
// crypto
#define Sha1_block_size 64
#define Sha1_digest_length 20
// time
#define Scalpi_time_ms_per_s 1000
// os
// network
#define WSA_SUCCESS 0
#define PRISOCK "%llx"
#define Os_network_tcp_Socket SOCKET
#define max_allowed_listen_port 49151
// Network
// Scalpi_network_tcp_http_WebSocket_Header
#define Scalpi_network_tcp_http_WebSocket_Header_Mask_size 4
// Scalpi_network_tcp_http_WebSocket_Header_Opcode
#define Scalpi_network_tcp_http_WebSocket_Header_Opcode_continuation 0
#define Scalpi_network_tcp_http_WebSocket_Header_Opcode_text 1
#define Scalpi_network_tcp_http_WebSocket_Header_Opcode_binary 2
#define Scalpi_network_tcp_http_WebSocket_Header_Opcode_close 8
#define Scalpi_network_tcp_http_WebSocket_Header_Opcode_ping 9
#define Scalpi_network_tcp_http_WebSocket_Header_Opcode_pong 10
// 2 bytes header for sizes 1 ... 125
#define Scalpi_network_tcp_http_WebSocket_Header_Size_small 0
#define Scalpi_network_tcp_http_WebSocket_Header_Size_small_header 2
#define Scalpi_network_tcp_http_WebSocket_Header_Size_small_extended_payload_len_size 0
// 4 bytes header for sizes 126 ... 65535
#define Scalpi_network_tcp_http_WebSocket_Header_Size_medium 1
#define Scalpi_network_tcp_http_WebSocket_Header_Size_medium_flag 126
#define Scalpi_network_tcp_http_WebSocket_Header_Size_medium_extended_payload_len_size 2
#define Scalpi_network_tcp_http_WebSocket_Header_Size_medium_header (Scalpi_network_tcp_http_WebSocket_Header_Size_small_header + Scalpi_network_tcp_http_WebSocket_Header_Size_medium_extended_payload_len_size)
// 10 bytes header for sizes more than 65535
#define Scalpi_network_tcp_http_WebSocket_Header_Size_long 2
#define Scalpi_network_tcp_http_WebSocket_Header_Size_long_flag 127
#define Scalpi_network_tcp_http_WebSocket_Header_Size_long_extended_payload_len_size 8
#define Scalpi_network_tcp_http_WebSocket_Header_Size_long_header (Scalpi_network_tcp_http_WebSocket_Header_Size_small_header + Scalpi_network_tcp_http_WebSocket_Header_Size_long_extended_payload_len_size)
// Scalpi_Console_ansi
#define Scalpi_Console_ansi_esc "\x1B"
#define Scalpi_Console_ansi_control Scalpi_Console_ansi_esc "["
#define Scalpi_Console_ansi_output_clear_to_end_line Scalpi_Console_ansi_control "0K"
#define Scalpi_Console_ansi_output_clear_to_start_line Scalpi_Console_ansi_control "1K"
#define Scalpi_Console_ansi_output_clear_line Scalpi_Console_ansi_control "2K"
// cursor
#define Scalpi_Console_ansi_output_cursor_hide Scalpi_Console_ansi_control "?25l"
#define Scalpi_Console_ansi_output_cursor_show Scalpi_Console_ansi_control "?25h"
#define Scalpi_Console_ansi_output_cursor_style_reset Scalpi_Console_ansi_control "0 q"
#define Scalpi_Console_ansi_output_cursor_style_blinking_block Scalpi_Console_ansi_control "1 q"
#define Scalpi_Console_ansi_output_cursor_style_steady_block Scalpi_Console_ansi_control "2 q"
#define Scalpi_Console_ansi_output_cursor_style_blinking_underline Scalpi_Console_ansi_control "3 q"
#define Scalpi_Console_ansi_output_cursor_style_steady_underline Scalpi_Console_ansi_control "4 q"
#define Scalpi_Console_ansi_output_cursor_style_blinking_I_beam Scalpi_Console_ansi_control "5 q"
#define Scalpi_Console_ansi_output_cursor_style_steady_I_beam Scalpi_Console_ansi_control "6 q"
// color
#define Scalpi_Console_ansi_output_FontStyle_color_zero "39"
#define Scalpi_Console_ansi_output_FontStyle_color_black "30"
#define Scalpi_Console_ansi_output_FontStyle_color_red "31"
#define Scalpi_Console_ansi_output_FontStyle_color_green "32"
#define Scalpi_Console_ansi_output_FontStyle_color_gold "33"
#define Scalpi_Console_ansi_output_FontStyle_color_blue "34"
#define Scalpi_Console_ansi_output_FontStyle_color_magenta "35"
#define Scalpi_Console_ansi_output_FontStyle_color_cyan "36"
#define Scalpi_Console_ansi_output_FontStyle_color_light_gray "37"
#define Scalpi_Console_ansi_output_FontStyle_color_gray "90"
#define Scalpi_Console_ansi_output_FontStyle_color_pink "91"
#define Scalpi_Console_ansi_output_FontStyle_color_light_green "92"
#define Scalpi_Console_ansi_output_FontStyle_color_yellow "93"
#define Scalpi_Console_ansi_output_FontStyle_color_light_blue "94"
#define Scalpi_Console_ansi_output_FontStyle_color_light_magenta "95"
#define Scalpi_Console_ansi_output_FontStyle_color_light_cyan "96"
#define Scalpi_Console_ansi_output_FontStyle_color_white "97"
// App
#define App_delay_to_start_frame_target (Scalpi_time_ms_per_s / App_expected_fps)
// settings
#define MEMORY_TYPE_ENDIANS Endians_Little
// App
#define App_expected_fps 30
// macroses
// c
#define or ||
#define and &&
#define OFFSETOF(type, field) ((size_t) &(((type *)0)->field))
#define FIELD_PARENT_PTR(type, field, instance) ((type *)((char *)(instance) - OFFSETOF(type, field)))
#define LENGTH(array) (sizeof(array) / sizeof(array[0]))
#define STR_LEN(str) (LENGTH(str) - 1)
// text
#define SLICE_TEXT(text) {.ptr=text, .len=STR_LEN(text)}
#define TEXT_PTR(text) (char(*)[STR_LEN(text)]) text
#define DEF_TEXT_PTR(name, text) char(*name)[STR_LEN(text)] = TEXT_PTR(text)
#define DEF_SLICE_TEXT(name, text) struct Slice name = SLICE_TEXT(text)
// types
// core
// memory
struct Slice {
char* ptr;
size_t len;
};
struct Finder {
struct Slice text;
struct Slice desired;
size_t pos;
};
// Os
struct Os_Console_Flags {
bool inited;
uint64_t input_mode;
uint64_t output_mode;
};
typedef void* Os_Console_Input_Handle;
struct Os_Console_Input {
Os_Console_Input_Handle handle;
};
typedef void* Os_Console_Output_Handle;
struct Os_Console_Output {
Os_Console_Output_Handle handle;
};
// Scalpi
// text
struct Scalpi_Text_Splitter {
struct Finder finder;
bool stop;
size_t last_pos;
};
// crypto
// Sha1_Streaming
struct Sha1_Streaming {
uint32_t state[5];
char block [Sha1_block_size];
size_t buf_len; // current filling level of block
uint64_t total_len;
};
struct RoundParam {uint32_t a; uint32_t b; uint32_t c; uint32_t d; uint32_t e; uint32_t i;};
// network
// Scalpi_network_tcp_Ip
// ip
struct Scalpi_network_tcp_Ip {
uint8_t address[4];
uint16_t port;
};
struct Scalpi_network_tcp_Ip_ToText {
/// 255.255.255.255:65535
/// 127. 0. 0. 1:27015
/// 0. 0. 0. 1: 0
char buffer[25];
size_t len;
};
struct Scalpi_network_tcp_Visitor {
Os_network_tcp_Socket socket_handle;
struct Scalpi_network_tcp_Ip ip;
struct sockaddr_in addr;
};
struct Scalpi_network_tcp_Server {
bool ready;
Os_network_tcp_Socket socket_handle;
size_t max_waiting_clients;
uint16_t port;
};
// Scalpi_network_tcp_http_WebSocket
// real struct of web socket header:
// fin_flag: u1 = 1,
// rsv: [3]u1 = .{0,0,0},
// opcode: Opcode = .binary, // may be text
// mask_flag: u1 = 0,
// payload_len: u7 = 0,
// extended_payload_len: u16 or u64 or void (optional)
// masking_key: u64 or void (optional)
struct Scalpi_network_tcp_http_WebSocket_Parser {
size_t frame_len;
size_t header_len;
char* message; // no null
size_t message_len;
};
// Os_Console
// Scalpi
struct Scalpi_Writer {
void* context;
bool (*write) (void* context, size_t* writted, char* bytes, size_t bytes_len);
};
struct Scalpi_Logger {
// write to terminal, console, file or to all together, but no real check "bytes is writen"
struct Scalpi_Writer* c_out_writer;
struct Scalpi_Writer* file_writer;
struct Scalpi_Writer* console_writer;
struct Scalpi_Writer* terminal_writer;
};
struct Scalpi_Console {
void* input;
void* output;
};
// App
struct App_WinApi {
HINSTANCE instance;
void* console_input_handle;
void* console_output_handle;
WSADATA wsdata;
};
struct App {
struct App_WinApi winapi;
struct Scalpi_Logger logger;
uint64_t tick;
};
// function prototypes
// globals
// crypto
// Sha1
const struct RoundParam round0a[] = {
{0, 1, 2, 3, 4, 0},
{4, 0, 1, 2, 3, 1},
{3, 4, 0, 1, 2, 2},
{2, 3, 4, 0, 1, 3},
{1, 2, 3, 4, 0, 4},
{0, 1, 2, 3, 4, 5},
{4, 0, 1, 2, 3, 6},
{3, 4, 0, 1, 2, 7},
{2, 3, 4, 0, 1, 8},
{1, 2, 3, 4, 0, 9},
{0, 1, 2, 3, 4, 10},
{4, 0, 1, 2, 3, 11},
{3, 4, 0, 1, 2, 12},
{2, 3, 4, 0, 1, 13},
{1, 2, 3, 4, 0, 14},
{0, 1, 2, 3, 4, 15},
};
struct RoundParam round0b[] = {
{4, 0, 1, 2, 3, 16},
{3, 4, 0, 1, 2, 17},
{2, 3, 4, 0, 1, 18},
{1, 2, 3, 4, 0, 19},
};
struct RoundParam round1[] = {
{0, 1, 2, 3, 4, 20},
{4, 0, 1, 2, 3, 21},
{3, 4, 0, 1, 2, 22},
{2, 3, 4, 0, 1, 23},
{1, 2, 3, 4, 0, 24},
{0, 1, 2, 3, 4, 25},
{4, 0, 1, 2, 3, 26},
{3, 4, 0, 1, 2, 27},
{2, 3, 4, 0, 1, 28},
{1, 2, 3, 4, 0, 29},
{0, 1, 2, 3, 4, 30},
{4, 0, 1, 2, 3, 31},
{3, 4, 0, 1, 2, 32},
{2, 3, 4, 0, 1, 33},
{1, 2, 3, 4, 0, 34},
{0, 1, 2, 3, 4, 35},
{4, 0, 1, 2, 3, 36},
{3, 4, 0, 1, 2, 37},
{2, 3, 4, 0, 1, 38},
{1, 2, 3, 4, 0, 39},
};
struct RoundParam round2[] = {
{0, 1, 2, 3, 4, 40},
{4, 0, 1, 2, 3, 41},
{3, 4, 0, 1, 2, 42},
{2, 3, 4, 0, 1, 43},
{1, 2, 3, 4, 0, 44},
{0, 1, 2, 3, 4, 45},
{4, 0, 1, 2, 3, 46},
{3, 4, 0, 1, 2, 47},
{2, 3, 4, 0, 1, 48},
{1, 2, 3, 4, 0, 49},
{0, 1, 2, 3, 4, 50},
{4, 0, 1, 2, 3, 51},
{3, 4, 0, 1, 2, 52},
{2, 3, 4, 0, 1, 53},
{1, 2, 3, 4, 0, 54},
{0, 1, 2, 3, 4, 55},
{4, 0, 1, 2, 3, 56},
{3, 4, 0, 1, 2, 57},
{2, 3, 4, 0, 1, 58},
{1, 2, 3, 4, 0, 59},
};
struct RoundParam round3[] = {
{0, 1, 2, 3, 4, 60},
{4, 0, 1, 2, 3, 61},
{3, 4, 0, 1, 2, 62},
{2, 3, 4, 0, 1, 63},
{1, 2, 3, 4, 0, 64},
{0, 1, 2, 3, 4, 65},
{4, 0, 1, 2, 3, 66},
{3, 4, 0, 1, 2, 67},
{2, 3, 4, 0, 1, 68},
{1, 2, 3, 4, 0, 69},
{0, 1, 2, 3, 4, 70},
{4, 0, 1, 2, 3, 71},
{3, 4, 0, 1, 2, 72},
{2, 3, 4, 0, 1, 73},
{1, 2, 3, 4, 0, 74},
{0, 1, 2, 3, 4, 75},
{4, 0, 1, 2, 3, 76},
{3, 4, 0, 1, 2, 77},
{2, 3, 4, 0, 1, 78},
{1, 2, 3, 4, 0, 79},
};
// formats
char* Base64_standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
struct App global_app;
// functions
// core
// mem
// bits operations
uint8_t bits_rotl8(uint8_t value, unsigned int shift) {
shift %= 8;
return (value << shift) | (value >> (8 - shift));
}
uint16_t bits_rotl16(uint16_t value, unsigned int shift) {
shift %= 16;
return (value << shift) | (value >> (16 - shift));
}
uint32_t bits_rotl32(uint32_t value, unsigned int shift) {
shift %= 32;
return (value << shift) | (value >> (32 - shift));
}
uint64_t bits_rotl64(uint64_t value, unsigned int shift) {
shift %= 64;
return (value << shift) | (value >> (64 - shift));
}
// bytes operations
void Scalpi_mem_set(char* dest, char symbol, size_t len) {
for (size_t pos = 0; pos < len; pos++) {
dest[pos] = symbol;
}
}
void Scalpi_mem_copy(char* dest, char* src, size_t size) {
// i use this instead memcpy becouse clang-in-zig just replace memcpy to crash-zig-bloated-version code
for(size_t pos = 0; pos < size; pos++) {
dest[pos] = src[pos];
}
}
bool Scalpi_mem_isEql(char* a, char* b, size_t len) {
for(size_t pos = 0; pos < len; pos ++) {
char ac = a[pos];
char bc = b[pos];
if (ac != bc) return false;
}
return true;
}
void writeWithOffset(char* dest, size_t* writed, char* data, size_t data_len) {
Scalpi_mem_copy(&dest[*writed], data, data_len);
*writed += data_len;
}
// format_number
// copy non aligned data
void memcpy2(char* dest, char* src) {
dest[0] = src[0];
dest[1] = src[1];
}
void memcpy4(char* dest, char* src) {
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest[3] = src[3];
}
void memcpy8(char* dest, char* src) {
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest[3] = src[3];
dest[4] = src[4];
dest[5] = src[5];
dest[6] = src[6];
dest[7] = src[7];
}
// swap bytes for converting endians
void swapBytes16(char* a, char* b) {
// b is output
b[0] = a[1];
b[1] = a[0];
}
void swapBytes32(char* a, char* b) {
// b is output
b[0] = a[3];
b[1] = a[2];
b[2] = a[1];
b[3] = a[0];
}
void swapBytes64(char* a, char* b) {
// b is output
b[0] = a[7];
b[1] = a[6];
b[2] = a[5];
b[3] = a[4];
b[4] = a[3];
b[5] = a[2];
b[6] = a[1];
b[7] = a[0];
}
bool cpu_checkNumberFormat() {
char current_endians = 0;
int n = 1;
if (* (char *) &n == 1) {
current_endians = Endians_Little;
} else {
current_endians = Endians_Big;
}
if (MEMORY_TYPE_ENDIANS == current_endians) {
return true;
} else {
printf("%d wrong MEMORY_TYPE !\r\n", __LINE__);
}
return false;
}
#if MEMORY_TYPE_ENDIANS == Endians_Big
#define nativeToBig16(src, dest) memcpy2(dest, src)
#define nativeToBig32(src, dest) memcpy4(dest, src)
#define nativeToBig64(src, dest) memcpy8(dest, src)
#define nativeToLittle16(src, dest) swapBytes16(src, dest)
#define nativeToLittle32(src, dest) swapBytes32(src, dest)
#define nativeToLittle64(src, dest) swapBytes64(src, dest)
#elif MEMORY_TYPE_ENDIANS == Endians_Little
#define nativeToBig16(src, dest) swapBytes16(src, dest)
#define nativeToBig32(src, dest) swapBytes32(src, dest)
#define nativeToBig64(src, dest) swapBytes64(src, dest)
#define nativeToLittle16(src, dest) memcpy2(dest, src)
#define nativeToLittle32(src, dest) memcpy4(dest, src)
#define nativeToLittle64(src, dest) memcpy8(dest, src)
#else
#error "please define MEMORY_TYPE_ENDIANS with Endians_Big or Endians_Little. (you can test endings with cpu_checkNumberFormat function)"
#endif
// read unaligned memory
// big
uint16_t readBig16(char* src) {
uint16_t ret;
nativeToBig16(src, (char*) &ret);
return ret;
}
uint32_t readBig32(char* src) {
uint32_t ret;
nativeToBig32(src, (char*) &ret);
return ret;
}
uint64_t readBig64(char* src) {
uint64_t ret;
nativeToBig64(src, (char*) &ret);
return ret;
}
// little
uint16_t readLittle16(char* src) {
uint16_t ret;
nativeToLittle16(src, (char*) &ret);
return ret;
}
uint32_t readLittle32(char* src) {
uint32_t ret;
nativeToLittle32(src, (char*) &ret);
return ret;
}
uint64_t readLittle64(char* src) {
uint64_t ret;
nativeToLittle64(src, (char*) &ret);
return ret;
}
// write unaligned memory
// big
void writeBig16(uint16_t value, char* dest) {
nativeToBig16((char*)&value, dest);
}
void writeBig32(uint32_t value, char* dest) {
nativeToBig32((char*)&value, dest);
}
void writeBig64(uint64_t value, char* dest) {
nativeToBig64((char*)&value, dest);
}
// little
void writeLittle16(uint16_t value, char* dest) {
nativeToLittle16((char*)&value, dest);
}
void writeLittle32(uint32_t value, char* dest) {
nativeToLittle32((char*)&value, dest);
}
void writeLittle64(uint64_t value, char* dest) {
nativeToLittle64((char*)&value, dest);
}
// Slice
void Slice_debug(struct Slice* slice, char* name, size_t line) {
if (slice->len == 0) {
printf("%zu: %s is NULL. \r\n", line, name);
} else {
printf("%zu: %s (%zu): \"%.*s", line, name, slice->len, (int)slice->len, slice->ptr);
}
}
bool Scalpi_mem_find(struct Slice* slice, struct Slice* desired, uintptr_t* out_pos) {
// not fast
if (slice->len >= desired->len) {
uintptr_t last_pos = slice->len - desired->len;
for (uintptr_t pos = 0; pos <= last_pos; pos++) {
size_t slice_len = slice->len - pos;
if (slice_len >= desired->len) {
if (Scalpi_mem_isEql(&slice->ptr[pos], desired->ptr, desired->len)) {
*out_pos = pos;
return true;
}
} else {
break;
}
}
} else {
// printf("%d desired is too long\r\n", __LINE__);
}
return false;
}
bool isStartWith(struct Slice* a, struct Slice* b) {
return (a->len >= b->len) && Scalpi_mem_isEql(a->ptr, b->ptr, b->len);
}
// Finder
void Finder_init(struct Finder* t, char* text, size_t text_len, char* desired, size_t desired_len) {
t->pos = 0;
t->text.ptr = text;
t->text.len = text_len;
t->desired.ptr = desired;
t->desired.len = desired_len;
}
bool Finder_next(struct Finder* t, size_t* out_pos) {
if (t->pos != t->text.len) {
struct Slice slice; // = t->text[->.pos..];
slice.ptr = &t->text.ptr[t->pos];
slice.len = t->text.len - t->pos;
size_t finded;
if (Scalpi_mem_find(&slice, &t->desired, &finded)) {
size_t new_pos = t->pos + finded;
t->pos = new_pos + t->desired.len;
*out_pos = new_pos;
return true;
}
}
return false;
}
// text Splitter
void Scalpi_Text_Splitter_init(struct Scalpi_Text_Splitter* t, char* text, size_t text_len, char* delim, size_t delim_len) {
t->stop = false;
t->last_pos = 0;
Finder_init(&t->finder, text, text_len, delim, delim_len);
}
bool Scalpi_Text_Splitter_next(struct Scalpi_Text_Splitter* t, struct Slice* out_slice) {
if (t->stop == false) {
size_t finded;
if (Finder_next(&t->finder, &finded)) {
out_slice->ptr = &t->finder.text.ptr[t->last_pos]; // slice = t->finder.text[t->last_pos..finded];
out_slice->len = finded - t->last_pos;
t->last_pos = t->finder.pos;
return true;
} else {
t->stop = true;
out_slice->ptr = &t->finder.text.ptr[t->last_pos]; // slice = t->finder.text[t->last_pos..];
out_slice->len = t->finder.text.len - t->last_pos;
t->last_pos = t->finder.pos;
return true;
}
}
return false;
}
// math
uint32_t ceilDiv32(uint32_t a, uint32_t b) {
return (a + b - 1) / b;
}
// formats
// Base64
uint32_t Base64_getEncodeSize(uint32_t src_len) {
int blocks = ceilDiv32(src_len, 3);
return blocks * 4;
}
void Base64_encode3Bytes(unsigned char in[3], char out[4]) {
out[0] = Base64_standard_alphabet_chars[(in[0] & 0b11111100) >> 2];
out[1] = Base64_standard_alphabet_chars[((in[0] & 0b00000011) << 4) | ((in[1] & 0b11110000) >> 4)];
out[2] = Base64_standard_alphabet_chars[((in[1] & 0b00001111) << 2) | ((in[2] & 0b11000000) >> 6)];
out[3] = Base64_standard_alphabet_chars[(in[2] & 0b00111111)];
}
bool test_rot() {
char a = 0b11111100 >> 2;
if (a == 0b00111111) {
return true;
} else {
printf("%d: failed test_rot.", __LINE__);
}
return false;
}
void Base64_encode(char* src, size_t src_len, char* out) {
// EXAMPLE
// Source ASCII text: "Ma"
// Character M a
// Octets 77 (0x4d) 97 (0x61)
// Bits 0 1 0 0 1 1 0 1|0 1 1 0 0 0 0 1|0 0 _ _ _ _ _ _|
// Base64 0 1 0 0 1 1|0 1 0 1 1 0|0 0 0 1 0 0|_ _ _ _ _ _|
// Sextets 19 22 4 Padding
// Character T W E =
// Octets 84 (0x54) 87 (0x57) 69 (0x45) 61 (0x3D)
// parse normal octets
char* end_of_src = src + src_len;
while(true) {
if (src + 3 > end_of_src) break;
Base64_encode3Bytes((void*) src, out);
src += 3;
out += 4;
}
// parse extra_bytes
size_t extra_bytes = end_of_src - src;
if (extra_bytes > 0 ) {
char b[3] = {0};
Scalpi_mem_copy(b, src, extra_bytes);
Base64_encode3Bytes((void*)b, out);
out += 4;
// add padding
size_t pads = 3 - extra_bytes;
for (char* pad = out - pads ;pad < out; pad++) {*pad = '=';}
}
}
// crypto
// HEX
#define bytesLenToHexLen(len) (len * 2)
#define hexLenToBytesLen(len) (len >> 1)
void printHex(char* buffer_ptr, uintptr_t buffer_len) {
for (int i = 0; i < buffer_len; i++) {
printf(" %02X ", buffer_ptr[i]);
}
}
// parse
char Hex_parseNibble(char nibble) {
if (nibble >= 'a') return 10 + nibble - 'a';
if (nibble >= 'A') return 10 + nibble - 'A';
return nibble - '0';
}
char Hex_parseByte(char* hex_byte) {
char b_F0 = Hex_parseNibble(hex_byte[0]);
char b_0F = Hex_parseNibble(hex_byte[1]);
return (b_F0 << 4) | b_0F;
}
void Hex_parse(char* hex, char* buffer_ptr, size_t hex_len) {
char parsed;
size_t end_pos = hex_len >> 1;
for(size_t pos = 0; pos < end_pos; pos++) {
char parsed = Hex_parseByte(&hex[pos*2]);
buffer_ptr[pos] = parsed;
}
}
// pack
// small hex
char Hex_packNibble_h(char data) {
if (data <= 9) return '0' + data;
if (data <= 15) return 'a' + data - 10;
return 0;
}
void Hex_packByte_h(char data, char* hex_byte) {
hex_byte[0] = Hex_packNibble_h((data & 0xF0) >> 4);
hex_byte[1] = Hex_packNibble_h(data & 0x0F);
}
void Hex_pack_h(char* hex, char* data, size_t data_len) {
// expected hex.len is bytesLenToHexLen(len)
for(size_t pos = 0; pos < data_len; pos++) {
hex[pos * 2] = 'A';
hex[pos * 2 + 1] = 'F';
Hex_packByte_h(data[pos], &hex[pos * 2]);
}
}
// capital letters
char Hex_packNibble_H(char data) {
if (data <= 9) return '0' + data;
if (data <= 15) return 'A' + data - 10;
return 0;
}
void Hex_packByte_H(char data, char* hex_byte) {
hex_byte[0] = Hex_packNibble_H((data & 0xF0) >> 4);
hex_byte[1] = Hex_packNibble_H(data & 0x0F);
}
void Hex_pack_H(char* hex, char* data, size_t data_len) {
// expected hex.len is bytesLenToHexLen(len)
for(size_t pos = 0; pos < data_len; pos++) {
Hex_packByte_H(data[pos], &hex[pos * 2]);
}
}
// Sha1
// Sha1_Streaming
void Sha1_Streaming_init(struct Sha1_Streaming* t) {
t->state[0] = 0x67452301;
t->state[1] = 0xEFCDAB89;
t->state[2] = 0x98BADCFE;
t->state[3] = 0x10325476;
t->state[4] = 0xC3D2E1F0;
t->total_len = 0;
t->buf_len = 0;
}
void Sha1_Streaming_round(struct Sha1_Streaming* d, char b[Sha1_block_size]) {
uint32_t s[16];
uint32_t v[5];
v[0] = d->state[0];
v[1] = d->state[1];
v[2] = d->state[2];
v[3] = d->state[3];
v[4] = d->state[4];
for (size_t pos = 0; pos < LENGTH(round0a); pos++) {
const struct RoundParam* r = &round0a[pos];
s[r->i] = readBig32(&b[r->i * 4]);
v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0x5A827999 + s[r->i & 0xf] + ((v[r->b] & v[r->c]) | (~v[r->b] & v[r->d]));
v[r->b] = bits_rotl32(v[r->b], 30);
}
for (size_t pos = 0; pos < LENGTH(round0b); pos++) {
struct RoundParam* r = &round0b[pos];
uint32_t t = s[(r->i - 3) & 0xf] ^ s[(r->i - 8) & 0xf] ^ s[(r->i - 14) & 0xf] ^ s[(r->i - 16) & 0xf];
s[r->i & 0xf] = bits_rotl32(t, 1);
v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0x5A827999 + s[r->i & 0xf] + ((v[r->b] & v[r->c]) | (~v[r->b] & v[r->d]));
v[r->b] = bits_rotl32(v[r->b], 30);
}
for (size_t pos = 0; pos < LENGTH(round1); pos++) {
struct RoundParam* r = &round1[pos];
uint32_t t = s[(r->i - 3) & 0xf] ^ s[(r->i - 8) & 0xf] ^ s[(r->i - 14) & 0xf] ^ s[(r->i - 16) & 0xf];
s[r->i & 0xf] = bits_rotl32(t, 1);
v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0x6ED9EBA1 + s[r->i & 0xf] + (v[r->b] ^ v[r->c] ^ v[r->d]);
v[r->b] = bits_rotl32(v[r->b], 30);
}
for (size_t pos = 0; pos < LENGTH(round2); pos++) {
struct RoundParam* r = &round2[pos];
uint32_t t = s[(r->i - 3) & 0xf] ^ s[(r->i - 8) & 0xf] ^ s[(r->i - 14) & 0xf] ^ s[(r->i - 16) & 0xf];
s[r->i & 0xf] = bits_rotl32(t, 1);
v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0x8F1BBCDC + s[r->i & 0xf] + ((v[r->b] & v[r->c]) ^ (v[r->b] & v[r->d]) ^ (v[r->c] & v[r->d]));
v[r->b] = bits_rotl32(v[r->b], 30);
}
for (size_t pos = 0; pos < LENGTH(round3); pos++) {
struct RoundParam* r = &round3[pos];
uint32_t t = s[(r->i - 3) & 0xf] ^ s[(r->i - 8) & 0xf] ^ s[(r->i - 14) & 0xf] ^ s[(r->i - 16) & 0xf];
s[r->i & 0xf] = bits_rotl32(t, 1);
v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0xCA62C1D6 + s[r->i & 0xf] + (v[r->b] ^ v[r->c] ^ v[r->d]);
v[r->b] = bits_rotl32(v[r->b], 30);
}
d->state[0] += v[0];
d->state[1] += v[1];
d->state[2] += v[2];
d->state[3] += v[3];
d->state[4] += v[4];
}
void Sha1_Streaming_do(struct Sha1_Streaming* t, char* data, size_t data_len) {
// add data to buffer and do Sha1 round if block is full
size_t pos = 0;
// if partial bAuffer exists from previous update and - copy into buffer.
// and if enough data to full block - then Sha1 round
if (t->buf_len != 0 and t->buf_len + data_len >= Sha1_block_size) {
pos += Sha1_block_size - t->buf_len;
Scalpi_mem_copy(&t->block[t->buf_len], data, pos);
Sha1_Streaming_round(t, t->block);
t->buf_len = 0;
}
// round all full middle blocks.
while (pos + Sha1_block_size <= data_len) {
Sha1_Streaming_round(t, &data[pos]);
pos += Sha1_block_size;
}
// Copy any remainder for next pass.
size_t remainder_len = data_len - pos;
if (remainder_len > 0) {
Scalpi_mem_copy(&t->block[t->buf_len], &data[pos], remainder_len);
t->buf_len += remainder_len;
}
t->total_len += data_len;
}
void Sha1_Streaming_finalize(struct Sha1_Streaming* t, char out[Sha1_digest_length]) {
// The buffer here will never be completely full.
Scalpi_mem_set(&t->block[t->buf_len], 0, Sha1_block_size - t->buf_len);
// Append padding bits.
t->block[t->buf_len] = 0x80;
t->buf_len += 1;
// > 448 mod 512 so need to add an extra round to wrap around.
if (Sha1_block_size - t->buf_len < sizeof(uint64_t)) {
Sha1_Streaming_round(t, t->block);
Scalpi_mem_set(t->block, 0, Sha1_block_size);
}
// Append message length.
writeBig64(t->total_len * 8, &t->block[Sha1_block_size - sizeof(uint64_t)]);
// last round
Sha1_Streaming_round(t, t->block);
// out
for (size_t j = 0; j < 5; j++) {
uint32_t s = t->state[j];
writeBig32(s, &out[4 * j]);
}
}
void Sha1_do(char out[Sha1_digest_length], char* input, size_t input_len) {
struct Sha1_Streaming hasher;
Sha1_Streaming_init(&hasher);
Sha1_Streaming_do(&hasher, input, input_len);
Sha1_Streaming_finalize(&hasher, out);
}
// tests
// test streaming
bool Sha1_testEquals(char sha1[Sha1_digest_length], char expected_hex[40]) {
char expected_bytes[Sha1_digest_length];
Hex_parse(expected_hex, expected_bytes, 40);
return Scalpi_mem_isEql(expected_bytes, sha1, Sha1_digest_length);
}
bool test_sha1_streaming() {
char sha1[Sha1_digest_length];
struct Sha1_Streaming sha1_hasher;
Sha1_Streaming_init(&sha1_hasher);
Sha1_Streaming_finalize(&sha1_hasher, sha1);
if (! Sha1_testEquals(sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709")) {
char as_hex[Sha1_digest_length * 2];
Hex_pack_h(as_hex, sha1, Sha1_digest_length);
printf("%d fail Sha1_testEquals \r\n expected da39a3ee5e6b4b0d3255bfef95601890afd80709\r\n getted %.*s\r\n \r\n", __LINE__, bytesLenToHexLen(Sha1_digest_length), &as_hex[0]);
return false;
}
DEF_SLICE_TEXT(abc, "abc");
//Sha1_do(sha1, abc.ptr, abc.len);
Sha1_Streaming_init(&sha1_hasher);
Sha1_Streaming_do(&sha1_hasher, abc.ptr, abc.len);
Sha1_Streaming_finalize(&sha1_hasher, sha1);
if(! Sha1_testEquals(sha1, "a9993e364706816aba3e25717850c26c9cd0d89d")) {
char as_hex[Sha1_digest_length * 2];
Hex_pack_h(as_hex, sha1, Sha1_digest_length);
printf("%d fail Sha1_testEquals \r\n expected a9993e364706816aba3e25717850c26c9cd0d89d\r\n getted %.*s\r\n \r\n", __LINE__, bytesLenToHexLen(Sha1_digest_length), &as_hex[0]);
printf("%d fail Sha1_testEquals\r\n", __LINE__);
return false;
}
return true;
}
// test single
bool Sha1_testEqualHash(char expected_hex[40], struct Slice* input) {
char sha1[Sha1_digest_length];
Sha1_do(sha1, input->ptr, input->len);
// struct Sha1_Streaming sha1_hasher;
// Sha1_Streaming_init(&sha1_hasher);
// Sha1_Streaming_do(&sha1_hasher, input->ptr, input->len);
// Sha1_Streaming_finalize(&sha1_hasher, sha1);
return Sha1_testEquals(sha1, expected_hex);
}
bool test_sha1_single() {
DEF_SLICE_TEXT(empty, "");
if(! Sha1_testEqualHash("da39a3ee5e6b4b0d3255bfef95601890afd80709", &empty)) {
printf("%d fail Sha1_testEqualHash\r\n", __LINE__);
return false;
}
DEF_SLICE_TEXT(abc, "abc");
if(! Sha1_testEqualHash("a9993e364706816aba3e25717850c26c9cd0d89d", &abc)) {
printf("%d fail Sha1_testEqualHash\r\n", __LINE__);
return false;
}
DEF_SLICE_TEXT(long_test_text, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
if (! Sha1_testEqualHash("a49b2446a02c645bf419f995b67091253a04a259", &long_test_text)) {
printf("%d fail Sha1_testEqualHash\r\n", __LINE__);
return false;
}
return true;
}
// all test
bool Sha1_test() {
if (test_sha1_streaming()) {
if (test_sha1_single()) {
return true;
} else {
printf("%d fail test_sha1_single\r\n", __LINE__);
}
} else {
printf("%d fail test_sha1_streaming\r\n", __LINE__);
}
return false;
}
char Scalpi_crypto_sumBytes(char* data, size_t data_len) {
char ret = 0;
for (size_t pos = 0; pos < data_len; pos++) {
ret += data[pos];
}
return ret;
}
char Scalpi_crypto_xorBytes(char* data, size_t data_len) {
char ret = 0;
for (size_t pos = 0; pos < data_len; pos++) {
ret ^= data[pos];
}
return ret;
}
void Scalpi_crypto_xorCyclic(char* dest, char* a, size_t dest_len, char* b, size_t b_len) {
// expected dest_len == a.len
size_t ci = 0; // cyclical iterator
for (size_t pos = 0; pos < dest_len; pos++) {
dest[pos] = a[pos] ^ b[ci];
ci += 1;
if (ci == b_len) ci = 0;
}
}
// Os
// time
uint64_t os_getTick() {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
uint64_t ft64;
memcpy8((char*) &ft64, (void*)&ft);
return ft64 / 10000; // in ft is intervals * 100 ns. to ms need * 100 / 1000000
}
void Os_sleep(uint64_t ms) {
Sleep(ms);
}
void os_print(uint8_t* bytes, uint64_t bytes_len) {
uint8_t* byte_ptr = bytes;
while (bytes_len > 0) {
putchar(*byte_ptr);
bytes_len -= 1;
byte_ptr += 1;
}
}
// void Os_Console_Output_init(&global_app.console.output, c.GetStdHandle(c.STD_OUTPUT_HANDLE));
// Scalpi lib
// Scalpi_logger
void Scalpi_logger_init (struct Scalpi_Logger* t) {
t->c_out_writer = 0;
t->file_writer = 0;
t->console_writer = 0;
t->terminal_writer = 0;
}
void Scalpi_logger_tryWrite(struct Scalpi_Logger* t, char bytes[], uint64_t bytes_len) {
size_t writed;
if (t->c_out_writer) {
struct Scalpi_Writer* w = t->c_out_writer;
w->write(w, &writed, bytes, bytes_len);
}
if (t->file_writer) {
struct Scalpi_Writer* w = t->file_writer;
w->write(w, &writed, bytes, bytes_len);
}
if (t->console_writer) {
struct Scalpi_Writer* w = t->console_writer;
w->write(w, &writed, bytes, bytes_len);
}
if (t->terminal_writer) {
struct Scalpi_Writer* w = t->terminal_writer;
w->write(w, &writed, bytes, bytes_len);
}
}
// App
// struct App_WinApi
bool App_WinApi_init(struct App_WinApi* winapi) {
winapi->instance = GetModuleHandleA(0);
if (winapi->instance) { // enable network
return true;
}
FreeLibrary(winapi->instance);
return false;
}
void App_WinApi_deinit(struct App_WinApi* winapi) {
FreeLibrary(winapi->instance);
}
void App_WinApi_process(struct App_WinApi* winapi) {
MSG msg;
size_t message_count = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
if (message_count == 0) return;
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
// struct App
bool App_init(struct App* t) {
Scalpi_logger_init(&t->logger); // preinit console output for debug
if (App_WinApi_init(&t->winapi)) {
t->tick = os_getTick();
return true;
// fallback
App_WinApi_deinit(&t->winapi);
} else {
printf("%d app.winapi not inited\r\n", __LINE__);
}
return false;
}
void App_deinit(struct App* app) {
App_WinApi_deinit(&app->winapi);
}
bool App_process(struct App* t) {
App_WinApi_process(&t->winapi);
return true;
}
void App_waitToNextFrame(struct App* t) {
uint64_t expected_frame_end_time = t->tick + App_delay_to_start_frame_target;
uint64_t resulting_frame_end_time = os_getTick();
if (resulting_frame_end_time > expected_frame_end_time + 120) {
uint64_t delta = resulting_frame_end_time - expected_frame_end_time;
printf("%d \t hitch detection: %zu delta: %zu ms \r\n", __LINE__, t->tick, delta);
}
uint64_t delay_sleep = 1;
if (resulting_frame_end_time < expected_frame_end_time) {
delay_sleep = expected_frame_end_time - resulting_frame_end_time;
}
Os_sleep(delay_sleep);
t->tick = resulting_frame_end_time + delay_sleep;
}
// main
bool tests() {
if ( true
and cpu_checkNumberFormat()
and Sha1_test()
and test_rot()
// and etc...
// and etc...
) {
return true;
} else {
printf("%d fail tests \r\n", __LINE__);
return false;
}
return true;
}
bool real_main(int args_len, char** args_ptr) {
if (tests()) {
if (App_init(&global_app)) {
while(true) {
if (!App_process(&global_app)) break;
App_waitToNextFrame(&global_app);
}
App_deinit(&global_app);
return true;
} else {
printf("%d fail: App_init\r\n", __LINE__);
}
} else {
printf("%d fail tests \r\n", __LINE__);
}
return false;
}
int main(int args_len, char** args_ptr) {
if (real_main(args_len, args_ptr)) return 0;
return 1;
}