Esteban Fuentealba 2 лет назад
Сommit
a98380ed83

+ 96 - 0
README.md

@@ -0,0 +1,96 @@
+# [MALVEKE] ***GAME BOY*** Link Camera
+
+## Introduction
+
+<div align="center">
+
+**Official** | **Unleashed** | **RogueMaster** | **Xtreme**
+:- | :- | :- | :- 
+[![FlipC.org](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero/badge?root=flipper_companion_apps/malveke_gb_link_camera&branch=main)](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero?root=flipper_companion_apps/malveke_gb_link_camera&branch=main)|[![FlipC.org](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero/badge?root=flipper_companion_apps/malveke_gb_link_camera&branch=main&firmware=unleashed)](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero?root=flipper_companion_apps/malveke_gb_link_camera&branch=main&firmware=unleashed)|[![FlipC.org](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero/badge?root=flipper_companion_apps/malveke_gb_link_camera&branch=main&firmware=roguemaster)](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero?root=flipper_companion_apps/malveke_gb_link_camera&branch=main&firmware=roguemaster)|[![FlipC.org](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero/badge?root=flipper_companion_apps/malveke_gb_link_camera&branch=main&firmware=xtreme)](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero?root=flipper_companion_apps/malveke_gb_link_camera&branch=main&firmware=xtreme)
+</div>
+
+Extract your  ***GAME BOY*** Camera picture via WIFI, so they can be easily shared with your phone, tablet or pc. Easy to use, just hook up to your ***GAME BOY*** and print as usual, the device will store the images and share them on a web server via WIFI. You will need a printer cable or gameboy Color link cable.
+<br>
+
+
+## Instructions for use.
+
+These instructions assume that you are starting at the Flipper Zero desktop. Otherwise, press the Back button until you are at the desktop.
+
+- Press the `OK` button on the Flipper to open the main menu.
+- Choose `Applications` from the menu.
+- Choose `GPIO` from the submenu.
+- Choose `[MALVEKE] GAME BOY Link-Camera`
+- The Flipper Zero will show the loading screen of the application. 
+
+    <p align='center'>
+        <br />
+        <img src="./docs/images/flipper-zero-flat-1.png" width="400" />
+        <br />
+    </p>
+
+- If the **MALVEKE** board is not connected, the following message will appear. You must connect it and press `OK`.
+
+    <p align='center'>
+        <br />
+        <img src="./docs/images/flipper-zero-flat-2.png" width="400" />
+        <br />
+    </p>
+
+- **MALVEKE** will create a Wi-Fi network using the data displayed on the screen.
+    <p align='center'>
+        <br />
+        <img src="./docs/images/flipper-zero-flat-3.png" width="400" />
+        <br />
+    </p>
+
+- On your computer or phone, search for the network and connect using the provided access credentials.
+    <p align='center'>
+        <br />
+        <img src="./docs/images/wifi-4.png" width="400" />
+        <br />
+    </p>
+    <p align='center'>
+        <br />
+        <img src="./docs/images/wifi-5.png" width="400" />
+        <br />
+    </p>
+
+- When you connect, it will automatically open a browser window with the host. If this doesn't happen, you should manually enter the IP provided on the Flipper Zero as the **host** in your browser.
+    <p align='center'>
+        <br />
+        <img src="./docs/images/captive-portal-6.png" width="400" />
+        <br />
+    </p>
+
+- Now, on your ***GAME BOY***, connect the Link Cable to **MALVEKE** `EXT2` port and proceed to print images as usual; they will appear in the web browser.
+    <p align='center'>
+        <br />
+        <img src="./docs/images/gbp-6.png" width="400" />
+        <br />
+    </p>
+
+    <p align='center'>
+        <br />
+        <img src="./docs/images/transfer-7.gif" width="400" />
+        <br />
+    </p>
+- In web you can Change palette and with button `A` Download image
+
+
+## Acknowledgements
+- [@brundonsmith](https://github.com/brundonsmith) for [GAME BOY Color in CSS](https://codepen.io/brundolf/pen/beagbQ).
+- [@mofosyne](https://github.com/mofosyne) Code to emulate a gameboy printer via the gameboy link cable.
+- [@HerrZatacke](https://github.com/HerrZatacke) I extracted many ideas from their repositories.
+
+## TODO
+- [ ] Refactor Code
+- [ ] Documentation
+- [ ] Save transfered imagen in Flipper Zero
+- [ ] Refactor Code
+
+<p align='center'>
+<br />
+<br />
+From Talcahuano 🇨🇱 with ❤ 
+</p>

+ 17 - 0
application.fam

@@ -0,0 +1,17 @@
+App(
+    appid="malveke_gb_link_camera",
+    name="[MALVEKE] GAME BOY Link-Camera",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="link_camera_app",
+    cdefines=["APP_EMULATOR"],
+    requires=[
+        "gui",
+        "storage",
+    ],
+    stack_size=1 * 1024,
+    order=10,
+    fap_libs=["assets"],
+    fap_icon="icons/boilerplate_10px.png",
+    fap_icon_assets="icons",
+    fap_category="GPIO",
+)

+ 2743 - 0
cJSON.c

@@ -0,0 +1,2743 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  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.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning(push)
+/* disable warning about single line comments in system headers */
+#pragma warning(disable : 4001)
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <float.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#ifdef _WIN32
+#define NAN sqrt(-1.0)
+#else
+#define NAN 0.0 / 0.0
+#endif
+#endif
+
+typedef struct {
+    const unsigned char* json;
+    size_t position;
+} error;
+static error global_error = {NULL, 0};
+
+CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void) {
+    return (const char*)(global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item) {
+    if(!cJSON_IsString(item)) {
+        return NULL;
+    }
+
+    return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item) {
+    if(!cJSON_IsNumber(item)) {
+        return (double)NAN;
+    }
+
+    return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if(CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15)
+#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void) {
+    static char version[15];
+    sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+    return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char* string1, const unsigned char* string2) {
+    if((string1 == NULL) || (string2 == NULL)) {
+        return 1;
+    }
+
+    if(string1 == string2) {
+        return 0;
+    }
+
+    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) {
+        if(*string1 == '\0') {
+            return 0;
+        }
+    }
+
+    return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks {
+    void*(CJSON_CDECL* allocate)(size_t size);
+    void(CJSON_CDECL* deallocate)(void* pointer);
+    void*(CJSON_CDECL* reallocate)(void* pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void* CJSON_CDECL internal_malloc(size_t size) {
+    return malloc(size);
+}
+static void CJSON_CDECL internal_free(void* pointer) {
+    free(pointer);
+}
+static void* CJSON_CDECL internal_realloc(void* pointer, size_t size) {
+    return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc};
+
+static unsigned char*
+    cJSON_strdup(const unsigned char* string, const internal_hooks* const hooks) {
+    size_t length = 0;
+    unsigned char* copy = NULL;
+
+    if(string == NULL) {
+        return NULL;
+    }
+
+    length = strlen((const char*)string) + sizeof("");
+    copy = (unsigned char*)hooks->allocate(length);
+    if(copy == NULL) {
+        return NULL;
+    }
+    memcpy(copy, string, length);
+
+    return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) {
+    if(hooks == NULL) {
+        /* Reset hooks */
+        global_hooks.allocate = malloc;
+        global_hooks.deallocate = free;
+        global_hooks.reallocate = realloc;
+        return;
+    }
+
+    global_hooks.allocate = malloc;
+    if(hooks->malloc_fn != NULL) {
+        global_hooks.allocate = hooks->malloc_fn;
+    }
+
+    global_hooks.deallocate = free;
+    if(hooks->free_fn != NULL) {
+        global_hooks.deallocate = hooks->free_fn;
+    }
+
+    /* use realloc only if both free and malloc are used */
+    global_hooks.reallocate = NULL;
+    if((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) {
+        global_hooks.reallocate = realloc;
+    }
+}
+
+/* Internal constructor. */
+static cJSON* cJSON_New_Item(const internal_hooks* const hooks) {
+    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+    if(node) {
+        memset(node, '\0', sizeof(cJSON));
+    }
+
+    return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON* item) {
+    cJSON* next = NULL;
+    while(item != NULL) {
+        next = item->next;
+        if(!(item->type & cJSON_IsReference) && (item->child != NULL)) {
+            cJSON_Delete(item->child);
+        }
+        if(!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) {
+            global_hooks.deallocate(item->valuestring);
+        }
+        if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) {
+            global_hooks.deallocate(item->string);
+        }
+        global_hooks.deallocate(item);
+        item = next;
+    }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void) {
+#ifdef ENABLE_LOCALES
+    struct lconv* lconv = localeconv();
+    return (unsigned char)lconv->decimal_point[0];
+#else
+    return '.';
+#endif
+}
+
+typedef struct {
+    const unsigned char* content;
+    size_t length;
+    size_t offset;
+    size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+    internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) \
+    ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) \
+    ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Converts an array of characters to double. Alternative implementation of strtod() */
+double string_to_double(const char* str, char** endptr) {
+    double result = 0.0;
+    int sign = 1;
+    const char* p = str;
+
+    while(isspace((unsigned char)*p)) p++;
+
+    if(*p == '-') {
+        sign = -1;
+        p++;
+    } else if(*p == '+') {
+        p++;
+    }
+
+    while(isdigit((unsigned char)*p)) {
+        result = result * (double)(10) + ((double)(*p - '0'));
+        p++;
+    }
+
+    if(*p == '.') {
+        double fraction = 0.1;
+        p++;
+
+        while(isdigit((unsigned char)p[0])) {
+            fraction *= 0.1L;
+            result += (p++[0] - '0') * fraction;
+        }
+    }
+
+    if(*p == 'e' || *p == 'E') {
+        int exponent = 0;
+        int exp_sign = 1;
+        p++;
+
+        if(*p == '-') {
+            exp_sign = -1;
+            p++;
+        } else if(*p == '+') {
+            p++;
+        }
+
+        while(isdigit((unsigned char)*p)) {
+            exponent = exponent * 10 + (*p - '0');
+            p++;
+        }
+
+        exponent *= exp_sign;
+        result *= pow(10, exponent);
+    }
+
+    *endptr = (char*)p;
+
+    return sign * result;
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON* const item, parse_buffer* const input_buffer) {
+    double number = 0;
+    unsigned char* after_end = NULL;
+    unsigned char number_c_string[64];
+    unsigned char decimal_point = get_decimal_point();
+    size_t i = 0;
+
+    if((input_buffer == NULL) || (input_buffer->content == NULL)) {
+        return false;
+    }
+
+    /* copy the number into a temporary buffer and replace '.' with the decimal point
+     * of the current locale (for string_to_double)
+     * This also takes care of '\0' not necessarily being available for marking the end of the input */
+    for(i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) {
+        switch(buffer_at_offset(input_buffer)[i]) {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case '+':
+        case '-':
+        case 'e':
+        case 'E':
+            number_c_string[i] = buffer_at_offset(input_buffer)[i];
+            break;
+
+        case '.':
+            number_c_string[i] = decimal_point;
+            break;
+
+        default:
+            goto loop_end;
+        }
+    }
+loop_end:
+    number_c_string[i] = '\0';
+
+    number = string_to_double((const char*)number_c_string, (char**)&after_end);
+    if(number_c_string == after_end) {
+        return false; /* parse_error */
+    }
+
+    item->valuedouble = number;
+
+    /* use saturation in case of overflow */
+    if(number >= INT_MAX) {
+        item->valueint = INT_MAX;
+    } else if(number <= (double)INT_MIN) {
+        item->valueint = INT_MIN;
+    } else {
+        item->valueint = (int)number;
+    }
+
+    item->type = cJSON_Number;
+
+    input_buffer->offset += (size_t)(after_end - number_c_string);
+    return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number) {
+    if(number >= INT_MAX) {
+        object->valueint = INT_MAX;
+    } else if(number <= (double)INT_MIN) {
+        object->valueint = INT_MIN;
+    } else {
+        object->valueint = (int)number;
+    }
+
+    return object->valuedouble = number;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring) {
+    char* copy = NULL;
+    /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+    if(!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) {
+        return NULL;
+    }
+    if(strlen(valuestring) <= strlen(object->valuestring)) {
+        strcpy(object->valuestring, valuestring);
+        return object->valuestring;
+    }
+    copy = (char*)cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+    if(copy == NULL) {
+        return NULL;
+    }
+    if(object->valuestring != NULL) {
+        cJSON_free(object->valuestring);
+    }
+    object->valuestring = copy;
+
+    return copy;
+}
+
+typedef struct {
+    unsigned char* buffer;
+    size_t length;
+    size_t offset;
+    size_t depth; /* current nesting depth (for formatted printing) */
+    cJSON_bool noalloc;
+    cJSON_bool format; /* is this print a formatted print */
+    internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer* const p, size_t needed) {
+    unsigned char* newbuffer = NULL;
+    size_t newsize = 0;
+
+    if((p == NULL) || (p->buffer == NULL)) {
+        return NULL;
+    }
+
+    if((p->length > 0) && (p->offset >= p->length)) {
+        /* make sure that offset is valid */
+        return NULL;
+    }
+
+    if(needed > INT_MAX) {
+        /* sizes bigger than INT_MAX are currently not supported */
+        return NULL;
+    }
+
+    needed += p->offset + 1;
+    if(needed <= p->length) {
+        return p->buffer + p->offset;
+    }
+
+    if(p->noalloc) {
+        return NULL;
+    }
+
+    /* calculate new buffer size */
+    if(needed > (INT_MAX / 2)) {
+        /* overflow of int, use INT_MAX if possible */
+        if(needed <= INT_MAX) {
+            newsize = INT_MAX;
+        } else {
+            return NULL;
+        }
+    } else {
+        newsize = needed * 2;
+    }
+
+    if(p->hooks.reallocate != NULL) {
+        /* reallocate with realloc if available */
+        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+        if(newbuffer == NULL) {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+    } else {
+        /* otherwise reallocate manually */
+        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+        if(!newbuffer) {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+
+        memcpy(newbuffer, p->buffer, p->offset + 1);
+        p->hooks.deallocate(p->buffer);
+    }
+    p->length = newsize;
+    p->buffer = newbuffer;
+
+    return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer* const buffer) {
+    const unsigned char* buffer_pointer = NULL;
+    if((buffer == NULL) || (buffer->buffer == NULL)) {
+        return;
+    }
+    buffer_pointer = buffer->buffer + buffer->offset;
+
+    buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b) {
+    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+    return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON* const item, printbuffer* const output_buffer) {
+    unsigned char* output_pointer = NULL;
+    double d = item->valuedouble;
+    int length = 0;
+    size_t i = 0;
+    unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+    unsigned char decimal_point = get_decimal_point();
+    double test = 0.0;
+
+    if(output_buffer == NULL) {
+        return false;
+    }
+
+    /* This checks for NaN and Infinity */
+    if(isnan(d) || isinf(d)) {
+        length = snprintf((char*)number_buffer, sizeof(number_buffer), "null");
+    } else {
+        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+        length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d);
+
+        /* Check whether the original double can be recovered */
+        if((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) {
+            /* If not, print with 17 decimal places of precision */
+            length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d);
+        }
+    }
+
+    /* sprintf failed or buffer overrun occurred */
+    if((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) {
+        return false;
+    }
+
+    /* reserve appropriate space in the output */
+    output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+    if(output_pointer == NULL) {
+        return false;
+    }
+
+    /* copy the printed number to the output and replace locale
+     * dependent decimal point with '.' */
+    for(i = 0; i < ((size_t)length); i++) {
+        if(number_buffer[i] == decimal_point) {
+            output_pointer[i] = '.';
+            continue;
+        }
+
+        output_pointer[i] = number_buffer[i];
+    }
+    output_pointer[i] = '\0';
+
+    output_buffer->offset += (size_t)length;
+
+    return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char* const input) {
+    unsigned int h = 0;
+    size_t i = 0;
+
+    for(i = 0; i < 4; i++) {
+        /* parse digit */
+        if((input[i] >= '0') && (input[i] <= '9')) {
+            h += (unsigned int)input[i] - '0';
+        } else if((input[i] >= 'A') && (input[i] <= 'F')) {
+            h += (unsigned int)10 + input[i] - 'A';
+        } else if((input[i] >= 'a') && (input[i] <= 'f')) {
+            h += (unsigned int)10 + input[i] - 'a';
+        } else /* invalid */
+        {
+            return 0;
+        }
+
+        if(i < 3) {
+            /* shift left to make place for the next nibble */
+            h = h << 4;
+        }
+    }
+
+    return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(
+    const unsigned char* const input_pointer,
+    const unsigned char* const input_end,
+    unsigned char** output_pointer) {
+    long unsigned int codepoint = 0;
+    unsigned int first_code = 0;
+    const unsigned char* first_sequence = input_pointer;
+    unsigned char utf8_length = 0;
+    unsigned char utf8_position = 0;
+    unsigned char sequence_length = 0;
+    unsigned char first_byte_mark = 0;
+
+    if((input_end - first_sequence) < 6) {
+        /* input ends unexpectedly */
+        goto fail;
+    }
+
+    /* get the first utf16 sequence */
+    first_code = parse_hex4(first_sequence + 2);
+
+    /* check that the code is valid */
+    if(((first_code >= 0xDC00) && (first_code <= 0xDFFF))) {
+        goto fail;
+    }
+
+    /* UTF16 surrogate pair */
+    if((first_code >= 0xD800) && (first_code <= 0xDBFF)) {
+        const unsigned char* second_sequence = first_sequence + 6;
+        unsigned int second_code = 0;
+        sequence_length = 12; /* \uXXXX\uXXXX */
+
+        if((input_end - second_sequence) < 6) {
+            /* input ends unexpectedly */
+            goto fail;
+        }
+
+        if((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) {
+            /* missing second half of the surrogate pair */
+            goto fail;
+        }
+
+        /* get the second utf16 sequence */
+        second_code = parse_hex4(second_sequence + 2);
+        /* check that the code is valid */
+        if((second_code < 0xDC00) || (second_code > 0xDFFF)) {
+            /* invalid second half of the surrogate pair */
+            goto fail;
+        }
+
+        /* calculate the unicode codepoint from the surrogate pair */
+        codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+    } else {
+        sequence_length = 6; /* \uXXXX */
+        codepoint = first_code;
+    }
+
+    /* encode as UTF-8
+     * takes at maximum 4 bytes to encode:
+     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+    if(codepoint < 0x80) {
+        /* normal ascii, encoding 0xxxxxxx */
+        utf8_length = 1;
+    } else if(codepoint < 0x800) {
+        /* two bytes, encoding 110xxxxx 10xxxxxx */
+        utf8_length = 2;
+        first_byte_mark = 0xC0; /* 11000000 */
+    } else if(codepoint < 0x10000) {
+        /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 3;
+        first_byte_mark = 0xE0; /* 11100000 */
+    } else if(codepoint <= 0x10FFFF) {
+        /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 4;
+        first_byte_mark = 0xF0; /* 11110000 */
+    } else {
+        /* invalid unicode codepoint */
+        goto fail;
+    }
+
+    /* encode as utf8 */
+    for(utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) {
+        /* 10xxxxxx */
+        (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+        codepoint >>= 6;
+    }
+    /* encode first byte */
+    if(utf8_length > 1) {
+        (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+    } else {
+        (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+    }
+
+    *output_pointer += utf8_length;
+
+    return sequence_length;
+
+fail:
+    return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON* const item, parse_buffer* const input_buffer) {
+    const unsigned char* input_pointer = buffer_at_offset(input_buffer) + 1;
+    const unsigned char* input_end = buffer_at_offset(input_buffer) + 1;
+    unsigned char* output_pointer = NULL;
+    unsigned char* output = NULL;
+
+    /* not a string */
+    if(buffer_at_offset(input_buffer)[0] != '\"') {
+        goto fail;
+    }
+
+    {
+        /* calculate approximate size of the output (overestimate) */
+        size_t allocation_length = 0;
+        size_t skipped_bytes = 0;
+        while(((size_t)(input_end - input_buffer->content) < input_buffer->length) &&
+              (*input_end != '\"')) {
+            /* is escape sequence */
+            if(input_end[0] == '\\') {
+                if((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) {
+                    /* prevent buffer overflow when last input character is a backslash */
+                    goto fail;
+                }
+                skipped_bytes++;
+                input_end++;
+            }
+            input_end++;
+        }
+        if(((size_t)(input_end - input_buffer->content) >= input_buffer->length) ||
+           (*input_end != '\"')) {
+            goto fail; /* string ended unexpectedly */
+        }
+
+        /* This is at most how much we need for the output */
+        allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+        output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+        if(output == NULL) {
+            goto fail; /* allocation failure */
+        }
+    }
+
+    output_pointer = output;
+    /* loop through the string literal */
+    while(input_pointer < input_end) {
+        if(*input_pointer != '\\') {
+            *output_pointer++ = *input_pointer++;
+        }
+        /* escape sequence */
+        else {
+            unsigned char sequence_length = 2;
+            if((input_end - input_pointer) < 1) {
+                goto fail;
+            }
+
+            switch(input_pointer[1]) {
+            case 'b':
+                *output_pointer++ = '\b';
+                break;
+            case 'f':
+                *output_pointer++ = '\f';
+                break;
+            case 'n':
+                *output_pointer++ = '\n';
+                break;
+            case 'r':
+                *output_pointer++ = '\r';
+                break;
+            case 't':
+                *output_pointer++ = '\t';
+                break;
+            case '\"':
+            case '\\':
+            case '/':
+                *output_pointer++ = input_pointer[1];
+                break;
+
+            /* UTF-16 literal */
+            case 'u':
+                sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+                if(sequence_length == 0) {
+                    /* failed to convert UTF16-literal to UTF-8 */
+                    goto fail;
+                }
+                break;
+
+            default:
+                goto fail;
+            }
+            input_pointer += sequence_length;
+        }
+    }
+
+    /* zero terminate the output */
+    *output_pointer = '\0';
+
+    item->type = cJSON_String;
+    item->valuestring = (char*)output;
+
+    input_buffer->offset = (size_t)(input_end - input_buffer->content);
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if(output != NULL) {
+        input_buffer->hooks.deallocate(output);
+    }
+
+    if(input_pointer != NULL) {
+        input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+    }
+
+    return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool
+    print_string_ptr(const unsigned char* const input, printbuffer* const output_buffer) {
+    const unsigned char* input_pointer = NULL;
+    unsigned char* output = NULL;
+    unsigned char* output_pointer = NULL;
+    size_t output_length = 0;
+    /* numbers of additional characters needed for escaping */
+    size_t escape_characters = 0;
+
+    if(output_buffer == NULL) {
+        return false;
+    }
+
+    /* empty string */
+    if(input == NULL) {
+        output = ensure(output_buffer, sizeof("\"\""));
+        if(output == NULL) {
+            return false;
+        }
+        strcpy((char*)output, "\"\"");
+
+        return true;
+    }
+
+    /* set "flag" to 1 if something needs to be escaped */
+    for(input_pointer = input; *input_pointer; input_pointer++) {
+        switch(*input_pointer) {
+        case '\"':
+        case '\\':
+        case '\b':
+        case '\f':
+        case '\n':
+        case '\r':
+        case '\t':
+            /* one character escape sequence */
+            escape_characters++;
+            break;
+        default:
+            if(*input_pointer < 32) {
+                /* UTF-16 escape sequence uXXXX */
+                escape_characters += 5;
+            }
+            break;
+        }
+    }
+    output_length = (size_t)(input_pointer - input) + escape_characters;
+
+    output = ensure(output_buffer, output_length + sizeof("\"\""));
+    if(output == NULL) {
+        return false;
+    }
+
+    /* no characters have to be escaped */
+    if(escape_characters == 0) {
+        output[0] = '\"';
+        memcpy(output + 1, input, output_length);
+        output[output_length + 1] = '\"';
+        output[output_length + 2] = '\0';
+
+        return true;
+    }
+
+    output[0] = '\"';
+    output_pointer = output + 1;
+    /* copy the string */
+    for(input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) {
+        if((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) {
+            /* normal character, copy */
+            *output_pointer = *input_pointer;
+        } else {
+            /* character needs to be escaped */
+            *output_pointer++ = '\\';
+            switch(*input_pointer) {
+            case '\\':
+                *output_pointer = '\\';
+                break;
+            case '\"':
+                *output_pointer = '\"';
+                break;
+            case '\b':
+                *output_pointer = 'b';
+                break;
+            case '\f':
+                *output_pointer = 'f';
+                break;
+            case '\n':
+                *output_pointer = 'n';
+                break;
+            case '\r':
+                *output_pointer = 'r';
+                break;
+            case '\t':
+                *output_pointer = 't';
+                break;
+            default:
+                /* escape and print as unicode codepoint */
+                snprintf((char*)output_pointer, 6, "u%04x", *input_pointer);
+                output_pointer += 4;
+                break;
+            }
+        }
+    }
+    output[output_length + 1] = '\"';
+    output[output_length + 2] = '\0';
+
+    return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON* const item, printbuffer* const p) {
+    return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer);
+static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer);
+static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer);
+static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer);
+static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer);
+static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer* buffer_skip_whitespace(parse_buffer* const buffer) {
+    if((buffer == NULL) || (buffer->content == NULL)) {
+        return NULL;
+    }
+
+    if(cannot_access_at_index(buffer, 0)) {
+        return buffer;
+    }
+
+    while(can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) {
+        buffer->offset++;
+    }
+
+    if(buffer->offset == buffer->length) {
+        buffer->offset--;
+    }
+
+    return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer* skip_utf8_bom(parse_buffer* const buffer) {
+    if((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) {
+        return NULL;
+    }
+
+    if(can_access_at_index(buffer, 4) &&
+       (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) {
+        buffer->offset += 3;
+    }
+
+    return buffer;
+}
+
+CJSON_PUBLIC(cJSON*)
+cJSON_ParseWithOpts(
+    const char* value,
+    const char** return_parse_end,
+    cJSON_bool require_null_terminated) {
+    size_t buffer_length;
+
+    if(NULL == value) {
+        return NULL;
+    }
+
+    /* Adding null character size due to require_null_terminated. */
+    buffer_length = strlen(value) + sizeof("");
+
+    return cJSON_ParseWithLengthOpts(
+        value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON*)
+cJSON_ParseWithLengthOpts(
+    const char* value,
+    size_t buffer_length,
+    const char** return_parse_end,
+    cJSON_bool require_null_terminated) {
+    parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}};
+    cJSON* item = NULL;
+
+    /* reset error position */
+    global_error.json = NULL;
+    global_error.position = 0;
+
+    if(value == NULL || 0 == buffer_length) {
+        goto fail;
+    }
+
+    buffer.content = (const unsigned char*)value;
+    buffer.length = buffer_length;
+    buffer.offset = 0;
+    buffer.hooks = global_hooks;
+
+    item = cJSON_New_Item(&global_hooks);
+    if(item == NULL) /* memory fail */
+    {
+        goto fail;
+    }
+
+    if(!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) {
+        /* parse failure. ep is set. */
+        goto fail;
+    }
+
+    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+    if(require_null_terminated) {
+        buffer_skip_whitespace(&buffer);
+        if((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') {
+            goto fail;
+        }
+    }
+    if(return_parse_end) {
+        *return_parse_end = (const char*)buffer_at_offset(&buffer);
+    }
+
+    return item;
+
+fail:
+    if(item != NULL) {
+        cJSON_Delete(item);
+    }
+
+    if(value != NULL) {
+        error local_error;
+        local_error.json = (const unsigned char*)value;
+        local_error.position = 0;
+
+        if(buffer.offset < buffer.length) {
+            local_error.position = buffer.offset;
+        } else if(buffer.length > 0) {
+            local_error.position = buffer.length - 1;
+        }
+
+        if(return_parse_end != NULL) {
+            *return_parse_end = (const char*)local_error.json + local_error.position;
+        }
+
+        global_error = local_error;
+    }
+
+    return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value) {
+    return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length) {
+    return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char*
+    print(const cJSON* const item, cJSON_bool format, const internal_hooks* const hooks) {
+    static const size_t default_buffer_size = 256;
+    printbuffer buffer[1];
+    unsigned char* printed = NULL;
+
+    memset(buffer, 0, sizeof(buffer));
+
+    /* create buffer */
+    buffer->buffer = (unsigned char*)hooks->allocate(default_buffer_size);
+    buffer->length = default_buffer_size;
+    buffer->format = format;
+    buffer->hooks = *hooks;
+    if(buffer->buffer == NULL) {
+        goto fail;
+    }
+
+    /* print the value */
+    if(!print_value(item, buffer)) {
+        goto fail;
+    }
+    update_offset(buffer);
+
+    /* check if reallocate is available */
+    if(hooks->reallocate != NULL) {
+        printed = (unsigned char*)hooks->reallocate(buffer->buffer, buffer->offset + 1);
+        if(printed == NULL) {
+            goto fail;
+        }
+        buffer->buffer = NULL;
+    } else /* otherwise copy the JSON over to a new buffer */
+    {
+        printed = (unsigned char*)hooks->allocate(buffer->offset + 1);
+        if(printed == NULL) {
+            goto fail;
+        }
+        memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+        printed[buffer->offset] = '\0'; /* just to be sure */
+
+        /* free the buffer */
+        hooks->deallocate(buffer->buffer);
+    }
+
+    return printed;
+
+fail:
+    if(buffer->buffer != NULL) {
+        hooks->deallocate(buffer->buffer);
+    }
+
+    if(printed != NULL) {
+        hooks->deallocate(printed);
+    }
+
+    return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item) {
+    return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item) {
+    return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt) {
+    printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}};
+
+    if(prebuffer < 0) {
+        return NULL;
+    }
+
+    p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+    if(!p.buffer) {
+        return NULL;
+    }
+
+    p.length = (size_t)prebuffer;
+    p.offset = 0;
+    p.noalloc = false;
+    p.format = fmt;
+    p.hooks = global_hooks;
+
+    if(!print_value(item, &p)) {
+        global_hooks.deallocate(p.buffer);
+        return NULL;
+    }
+
+    return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format) {
+    printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}};
+
+    if((length < 0) || (buffer == NULL)) {
+        return false;
+    }
+
+    p.buffer = (unsigned char*)buffer;
+    p.length = (size_t)length;
+    p.offset = 0;
+    p.noalloc = true;
+    p.format = format;
+    p.hooks = global_hooks;
+
+    return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer) {
+    if((input_buffer == NULL) || (input_buffer->content == NULL)) {
+        return false; /* no input */
+    }
+
+    /* parse the different types of values */
+    /* null */
+    if(can_read(input_buffer, 4) &&
+       (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) {
+        item->type = cJSON_NULL;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* false */
+    if(can_read(input_buffer, 5) &&
+       (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) {
+        item->type = cJSON_False;
+        input_buffer->offset += 5;
+        return true;
+    }
+    /* true */
+    if(can_read(input_buffer, 4) &&
+       (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) {
+        item->type = cJSON_True;
+        item->valueint = 1;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* string */
+    if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) {
+        return parse_string(item, input_buffer);
+    }
+    /* number */
+    if(can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') ||
+                                                ((buffer_at_offset(input_buffer)[0] >= '0') &&
+                                                 (buffer_at_offset(input_buffer)[0] <= '9')))) {
+        return parse_number(item, input_buffer);
+    }
+    /* array */
+    if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) {
+        return parse_array(item, input_buffer);
+    }
+    /* object */
+    if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) {
+        return parse_object(item, input_buffer);
+    }
+
+    return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer) {
+    unsigned char* output = NULL;
+
+    if((item == NULL) || (output_buffer == NULL)) {
+        return false;
+    }
+
+    switch((item->type) & 0xFF) {
+    case cJSON_NULL:
+        output = ensure(output_buffer, 5);
+        if(output == NULL) {
+            return false;
+        }
+        strcpy((char*)output, "null");
+        return true;
+
+    case cJSON_False:
+        output = ensure(output_buffer, 6);
+        if(output == NULL) {
+            return false;
+        }
+        strcpy((char*)output, "false");
+        return true;
+
+    case cJSON_True:
+        output = ensure(output_buffer, 5);
+        if(output == NULL) {
+            return false;
+        }
+        strcpy((char*)output, "true");
+        return true;
+
+    case cJSON_Number:
+        return print_number(item, output_buffer);
+
+    case cJSON_Raw: {
+        size_t raw_length = 0;
+        if(item->valuestring == NULL) {
+            return false;
+        }
+
+        raw_length = strlen(item->valuestring) + sizeof("");
+        output = ensure(output_buffer, raw_length);
+        if(output == NULL) {
+            return false;
+        }
+        memcpy(output, item->valuestring, raw_length);
+        return true;
+    }
+
+    case cJSON_String:
+        return print_string(item, output_buffer);
+
+    case cJSON_Array:
+        return print_array(item, output_buffer);
+
+    case cJSON_Object:
+        return print_object(item, output_buffer);
+
+    default:
+        return false;
+    }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer) {
+    cJSON* head = NULL; /* head of the linked list */
+    cJSON* current_item = NULL;
+
+    if(input_buffer->depth >= CJSON_NESTING_LIMIT) {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if(buffer_at_offset(input_buffer)[0] != '[') {
+        /* not an array */
+        goto fail;
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) {
+        /* empty array */
+        goto success;
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if(cannot_access_at_index(input_buffer, 0)) {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do {
+        /* allocate next item */
+        cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if(new_item == NULL) {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if(head == NULL) {
+            /* start the linked list */
+            current_item = head = new_item;
+        } else {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse next value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if(!parse_value(current_item, input_buffer)) {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if(cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') {
+        goto fail; /* expected end of array */
+    }
+
+success:
+    input_buffer->depth--;
+
+    if(head != NULL) {
+        head->prev = current_item;
+    }
+
+    item->type = cJSON_Array;
+    item->child = head;
+
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if(head != NULL) {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer) {
+    unsigned char* output_pointer = NULL;
+    size_t length = 0;
+    cJSON* current_element = item->child;
+
+    if(output_buffer == NULL) {
+        return false;
+    }
+
+    /* Compose the output array. */
+    /* opening square bracket */
+    output_pointer = ensure(output_buffer, 1);
+    if(output_pointer == NULL) {
+        return false;
+    }
+
+    *output_pointer = '[';
+    output_buffer->offset++;
+    output_buffer->depth++;
+
+    while(current_element != NULL) {
+        if(!print_value(current_element, output_buffer)) {
+            return false;
+        }
+        update_offset(output_buffer);
+        if(current_element->next) {
+            length = (size_t)(output_buffer->format ? 2 : 1);
+            output_pointer = ensure(output_buffer, length + 1);
+            if(output_pointer == NULL) {
+                return false;
+            }
+            *output_pointer++ = ',';
+            if(output_buffer->format) {
+                *output_pointer++ = ' ';
+            }
+            *output_pointer = '\0';
+            output_buffer->offset += length;
+        }
+        current_element = current_element->next;
+    }
+
+    output_pointer = ensure(output_buffer, 2);
+    if(output_pointer == NULL) {
+        return false;
+    }
+    *output_pointer++ = ']';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer) {
+    cJSON* head = NULL; /* linked list head */
+    cJSON* current_item = NULL;
+
+    if(input_buffer->depth >= CJSON_NESTING_LIMIT) {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) {
+        goto fail; /* not an object */
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) {
+        goto success; /* empty object */
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if(cannot_access_at_index(input_buffer, 0)) {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do {
+        /* allocate next item */
+        cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if(new_item == NULL) {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if(head == NULL) {
+            /* start the linked list */
+            current_item = head = new_item;
+        } else {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse the name of the child */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if(!parse_string(current_item, input_buffer)) {
+            goto fail; /* failed to parse name */
+        }
+        buffer_skip_whitespace(input_buffer);
+
+        /* swap valuestring and string, because we parsed the name */
+        current_item->string = current_item->valuestring;
+        current_item->valuestring = NULL;
+
+        if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) {
+            goto fail; /* invalid object */
+        }
+
+        /* parse the value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if(!parse_value(current_item, input_buffer)) {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) {
+        goto fail; /* expected end of object */
+    }
+
+success:
+    input_buffer->depth--;
+
+    if(head != NULL) {
+        head->prev = current_item;
+    }
+
+    item->type = cJSON_Object;
+    item->child = head;
+
+    input_buffer->offset++;
+    return true;
+
+fail:
+    if(head != NULL) {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer) {
+    unsigned char* output_pointer = NULL;
+    size_t length = 0;
+    cJSON* current_item = item->child;
+
+    if(output_buffer == NULL) {
+        return false;
+    }
+
+    /* Compose the output: */
+    length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */
+    output_pointer = ensure(output_buffer, length + 1);
+    if(output_pointer == NULL) {
+        return false;
+    }
+
+    *output_pointer++ = '{';
+    output_buffer->depth++;
+    if(output_buffer->format) {
+        *output_pointer++ = '\n';
+    }
+    output_buffer->offset += length;
+
+    while(current_item) {
+        if(output_buffer->format) {
+            size_t i;
+            output_pointer = ensure(output_buffer, output_buffer->depth);
+            if(output_pointer == NULL) {
+                return false;
+            }
+            for(i = 0; i < output_buffer->depth; i++) {
+                *output_pointer++ = '\t';
+            }
+            output_buffer->offset += output_buffer->depth;
+        }
+
+        /* print key */
+        if(!print_string_ptr((unsigned char*)current_item->string, output_buffer)) {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        length = (size_t)(output_buffer->format ? 2 : 1);
+        output_pointer = ensure(output_buffer, length);
+        if(output_pointer == NULL) {
+            return false;
+        }
+        *output_pointer++ = ':';
+        if(output_buffer->format) {
+            *output_pointer++ = '\t';
+        }
+        output_buffer->offset += length;
+
+        /* print value */
+        if(!print_value(current_item, output_buffer)) {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        /* print comma if not last */
+        length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+        output_pointer = ensure(output_buffer, length + 1);
+        if(output_pointer == NULL) {
+            return false;
+        }
+        if(current_item->next) {
+            *output_pointer++ = ',';
+        }
+
+        if(output_buffer->format) {
+            *output_pointer++ = '\n';
+        }
+        *output_pointer = '\0';
+        output_buffer->offset += length;
+
+        current_item = current_item->next;
+    }
+
+    output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+    if(output_pointer == NULL) {
+        return false;
+    }
+    if(output_buffer->format) {
+        size_t i;
+        for(i = 0; i < (output_buffer->depth - 1); i++) {
+            *output_pointer++ = '\t';
+        }
+    }
+    *output_pointer++ = '}';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array) {
+    cJSON* child = NULL;
+    size_t size = 0;
+
+    if(array == NULL) {
+        return 0;
+    }
+
+    child = array->child;
+
+    while(child != NULL) {
+        size++;
+        child = child->next;
+    }
+
+    /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+    return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON* array, size_t index) {
+    cJSON* current_child = NULL;
+
+    if(array == NULL) {
+        return NULL;
+    }
+
+    current_child = array->child;
+    while((current_child != NULL) && (index > 0)) {
+        index--;
+        current_child = current_child->next;
+    }
+
+    return current_child;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index) {
+    if(index < 0) {
+        return NULL;
+    }
+
+    return get_array_item(array, (size_t)index);
+}
+
+static cJSON* get_object_item(
+    const cJSON* const object,
+    const char* const name,
+    const cJSON_bool case_sensitive) {
+    cJSON* current_element = NULL;
+
+    if((object == NULL) || (name == NULL)) {
+        return NULL;
+    }
+
+    current_element = object->child;
+    if(case_sensitive) {
+        while((current_element != NULL) && (current_element->string != NULL) &&
+              (strcmp(name, current_element->string) != 0)) {
+            current_element = current_element->next;
+        }
+    } else {
+        while((current_element != NULL) &&
+              (case_insensitive_strcmp(
+                   (const unsigned char*)name, (const unsigned char*)(current_element->string)) !=
+               0)) {
+            current_element = current_element->next;
+        }
+    }
+
+    if((current_element == NULL) || (current_element->string == NULL)) {
+        return NULL;
+    }
+
+    return current_element;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string) {
+    return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON*)
+cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string) {
+    return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string) {
+    return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON* prev, cJSON* item) {
+    prev->next = item;
+    item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON* create_reference(const cJSON* item, const internal_hooks* const hooks) {
+    cJSON* reference = NULL;
+    if(item == NULL) {
+        return NULL;
+    }
+
+    reference = cJSON_New_Item(hooks);
+    if(reference == NULL) {
+        return NULL;
+    }
+
+    memcpy(reference, item, sizeof(cJSON));
+    reference->string = NULL;
+    reference->type |= cJSON_IsReference;
+    reference->next = reference->prev = NULL;
+    return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON* array, cJSON* item) {
+    cJSON* child = NULL;
+
+    if((item == NULL) || (array == NULL) || (array == item)) {
+        return false;
+    }
+
+    child = array->child;
+    /*
+     * To find the last item in array quickly, we use prev in array
+     */
+    if(child == NULL) {
+        /* list is empty, start new one */
+        array->child = item;
+        item->prev = item;
+        item->next = NULL;
+    } else {
+        /* append to the end */
+        if(child->prev) {
+            suffix_object(child->prev, item);
+            array->child->prev = item;
+        }
+    }
+
+    return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item) {
+    return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || \
+    (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+#pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string) {
+    return (void*)string;
+}
+#if defined(__clang__) || \
+    (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+#pragma GCC diagnostic pop
+#endif
+
+static cJSON_bool add_item_to_object(
+    cJSON* const object,
+    const char* const string,
+    cJSON* const item,
+    const internal_hooks* const hooks,
+    const cJSON_bool constant_key) {
+    char* new_key = NULL;
+    int new_type = cJSON_Invalid;
+
+    if((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) {
+        return false;
+    }
+
+    if(constant_key) {
+        new_key = (char*)cast_away_const(string);
+        new_type = item->type | cJSON_StringIsConst;
+    } else {
+        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+        if(new_key == NULL) {
+            return false;
+        }
+
+        new_type = item->type & ~cJSON_StringIsConst;
+    }
+
+    if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) {
+        hooks->deallocate(item->string);
+    }
+
+    item->string = new_key;
+    item->type = new_type;
+
+    return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item) {
+    return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item) {
+    return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item) {
+    if(array == NULL) {
+        return false;
+    }
+
+    return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item) {
+    if((object == NULL) || (string == NULL)) {
+        return false;
+    }
+
+    return add_item_to_object(
+        object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name) {
+    cJSON* null = cJSON_CreateNull();
+    if(add_item_to_object(object, name, null, &global_hooks, false)) {
+        return null;
+    }
+
+    cJSON_Delete(null);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name) {
+    cJSON* true_item = cJSON_CreateTrue();
+    if(add_item_to_object(object, name, true_item, &global_hooks, false)) {
+        return true_item;
+    }
+
+    cJSON_Delete(true_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name) {
+    cJSON* false_item = cJSON_CreateFalse();
+    if(add_item_to_object(object, name, false_item, &global_hooks, false)) {
+        return false_item;
+    }
+
+    cJSON_Delete(false_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*)
+cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean) {
+    cJSON* bool_item = cJSON_CreateBool(boolean);
+    if(add_item_to_object(object, name, bool_item, &global_hooks, false)) {
+        return bool_item;
+    }
+
+    cJSON_Delete(bool_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*)
+cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number) {
+    cJSON* number_item = cJSON_CreateNumber(number);
+    if(add_item_to_object(object, name, number_item, &global_hooks, false)) {
+        return number_item;
+    }
+
+    cJSON_Delete(number_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*)
+cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string) {
+    cJSON* string_item = cJSON_CreateString(string);
+    if(add_item_to_object(object, name, string_item, &global_hooks, false)) {
+        return string_item;
+    }
+
+    cJSON_Delete(string_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*)
+cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw) {
+    cJSON* raw_item = cJSON_CreateRaw(raw);
+    if(add_item_to_object(object, name, raw_item, &global_hooks, false)) {
+        return raw_item;
+    }
+
+    cJSON_Delete(raw_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name) {
+    cJSON* object_item = cJSON_CreateObject();
+    if(add_item_to_object(object, name, object_item, &global_hooks, false)) {
+        return object_item;
+    }
+
+    cJSON_Delete(object_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name) {
+    cJSON* array = cJSON_CreateArray();
+    if(add_item_to_object(object, name, array, &global_hooks, false)) {
+        return array;
+    }
+
+    cJSON_Delete(array);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item) {
+    if((parent == NULL) || (item == NULL)) {
+        return NULL;
+    }
+
+    if(item != parent->child) {
+        /* not the first element */
+        item->prev->next = item->next;
+    }
+    if(item->next != NULL) {
+        /* not the last element */
+        item->next->prev = item->prev;
+    }
+
+    if(item == parent->child) {
+        /* first element */
+        parent->child = item->next;
+    } else if(item->next == NULL) {
+        /* last element */
+        parent->child->prev = item->prev;
+    }
+
+    /* make sure the detached item doesn't point anywhere anymore */
+    item->prev = NULL;
+    item->next = NULL;
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which) {
+    if(which < 0) {
+        return NULL;
+    }
+
+    return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which) {
+    cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string) {
+    cJSON* to_detach = cJSON_GetObjectItem(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string) {
+    cJSON* to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string) {
+    cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string) {
+    cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON* array, int which, cJSON* newitem) {
+    cJSON* after_inserted = NULL;
+
+    if(which < 0) {
+        return false;
+    }
+
+    after_inserted = get_array_item(array, (size_t)which);
+    if(after_inserted == NULL) {
+        return add_item_to_array(array, newitem);
+    }
+
+    newitem->next = after_inserted;
+    newitem->prev = after_inserted->prev;
+    after_inserted->prev = newitem;
+    if(after_inserted == array->child) {
+        array->child = newitem;
+    } else {
+        newitem->prev->next = newitem;
+    }
+    return true;
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement) {
+    if((parent == NULL) || (replacement == NULL) || (item == NULL)) {
+        return false;
+    }
+
+    if(replacement == item) {
+        return true;
+    }
+
+    replacement->next = item->next;
+    replacement->prev = item->prev;
+
+    if(replacement->next != NULL) {
+        replacement->next->prev = replacement;
+    }
+    if(parent->child == item) {
+        if(parent->child->prev == parent->child) {
+            replacement->prev = replacement;
+        }
+        parent->child = replacement;
+    } else { /*
+         * To find the last item in array quickly, we use prev in array.
+         * We can't modify the last item's next pointer where this item was the parent's child
+         */
+        if(replacement->prev != NULL) {
+            replacement->prev->next = replacement;
+        }
+        if(replacement->next == NULL) {
+            parent->child->prev = replacement;
+        }
+    }
+
+    item->next = NULL;
+    item->prev = NULL;
+    cJSON_Delete(item);
+
+    return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem) {
+    if(which < 0) {
+        return false;
+    }
+
+    return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(
+    cJSON* object,
+    const char* string,
+    cJSON* replacement,
+    cJSON_bool case_sensitive) {
+    if((replacement == NULL) || (string == NULL)) {
+        return false;
+    }
+
+    /* replace the name in the replacement */
+    if(!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) {
+        cJSON_free(replacement->string);
+    }
+    replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+    replacement->type &= ~cJSON_StringIsConst;
+
+    return cJSON_ReplaceItemViaPointer(
+        object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem) {
+    return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem) {
+    return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = cJSON_NULL;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = cJSON_True;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = boolean ? cJSON_True : cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = cJSON_Number;
+        item->valuedouble = num;
+
+        /* use saturation in case of overflow */
+        if(num >= INT_MAX) {
+            item->valueint = INT_MAX;
+        } else if(num <= (double)INT_MIN) {
+            item->valueint = INT_MIN;
+        } else {
+            item->valueint = (int)num;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = cJSON_String;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+        if(!item->valuestring) {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item != NULL) {
+        item->type = cJSON_String | cJSON_IsReference;
+        item->valuestring = (char*)cast_away_const(string);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item != NULL) {
+        item->type = cJSON_Object | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item != NULL) {
+        item->type = cJSON_Array | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = cJSON_Raw;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+        if(!item->valuestring) {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = cJSON_Array;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void) {
+    cJSON* item = cJSON_New_Item(&global_hooks);
+    if(item) {
+        item->type = cJSON_Object;
+    }
+
+    return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count) {
+    size_t i = 0;
+    cJSON* n = NULL;
+    cJSON* p = NULL;
+    cJSON* a = NULL;
+
+    if((count < 0) || (numbers == NULL)) {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0; a && (i < (size_t)count); i++) {
+        n = cJSON_CreateNumber(numbers[i]);
+        if(!n) {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i) {
+            a->child = n;
+        } else {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    if(a && a->child) {
+        a->child->prev = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count) {
+    size_t i = 0;
+    cJSON* n = NULL;
+    cJSON* p = NULL;
+    cJSON* a = NULL;
+
+    if((count < 0) || (numbers == NULL)) {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0; a && (i < (size_t)count); i++) {
+        n = cJSON_CreateNumber((double)numbers[i]);
+        if(!n) {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i) {
+            a->child = n;
+        } else {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    if(a && a->child) {
+        a->child->prev = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count) {
+    size_t i = 0;
+    cJSON* n = NULL;
+    cJSON* p = NULL;
+    cJSON* a = NULL;
+
+    if((count < 0) || (numbers == NULL)) {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0; a && (i < (size_t)count); i++) {
+        n = cJSON_CreateNumber(numbers[i]);
+        if(!n) {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i) {
+            a->child = n;
+        } else {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    if(a && a->child) {
+        a->child->prev = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count) {
+    size_t i = 0;
+    cJSON* n = NULL;
+    cJSON* p = NULL;
+    cJSON* a = NULL;
+
+    if((count < 0) || (strings == NULL)) {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0; a && (i < (size_t)count); i++) {
+        n = cJSON_CreateString(strings[i]);
+        if(!n) {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i) {
+            a->child = n;
+        } else {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    if(a && a->child) {
+        a->child->prev = n;
+    }
+
+    return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse) {
+    cJSON* newitem = NULL;
+    cJSON* child = NULL;
+    cJSON* next = NULL;
+    cJSON* newchild = NULL;
+
+    /* Bail on bad ptr */
+    if(!item) {
+        goto fail;
+    }
+    /* Create new item */
+    newitem = cJSON_New_Item(&global_hooks);
+    if(!newitem) {
+        goto fail;
+    }
+    /* Copy over all vars */
+    newitem->type = item->type & (~cJSON_IsReference);
+    newitem->valueint = item->valueint;
+    newitem->valuedouble = item->valuedouble;
+    if(item->valuestring) {
+        newitem->valuestring =
+            (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+        if(!newitem->valuestring) {
+            goto fail;
+        }
+    }
+    if(item->string) {
+        newitem->string = (item->type & cJSON_StringIsConst) ?
+                              item->string :
+                              (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+        if(!newitem->string) {
+            goto fail;
+        }
+    }
+    /* If non-recursive, then we're done! */
+    if(!recurse) {
+        return newitem;
+    }
+    /* Walk the ->next chain for the child. */
+    child = item->child;
+    while(child != NULL) {
+        newchild = cJSON_Duplicate(
+            child, true); /* Duplicate (with recurse) each item in the ->next chain */
+        if(!newchild) {
+            goto fail;
+        }
+        if(next != NULL) {
+            /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+            next->next = newchild;
+            newchild->prev = next;
+            next = newchild;
+        } else {
+            /* Set newitem->child and move to it */
+            newitem->child = newchild;
+            next = newchild;
+        }
+        child = child->next;
+    }
+    if(newitem && newitem->child) {
+        newitem->child->prev = newchild;
+    }
+
+    return newitem;
+
+fail:
+    if(newitem != NULL) {
+        cJSON_Delete(newitem);
+    }
+
+    return NULL;
+}
+
+static void skip_oneline_comment(char** input) {
+    *input += static_strlen("//");
+
+    for(; (*input)[0] != '\0'; ++(*input)) {
+        if((*input)[0] == '\n') {
+            *input += static_strlen("\n");
+            return;
+        }
+    }
+}
+
+static void skip_multiline_comment(char** input) {
+    *input += static_strlen("/*");
+
+    for(; (*input)[0] != '\0'; ++(*input)) {
+        if(((*input)[0] == '*') && ((*input)[1] == '/')) {
+            *input += static_strlen("*/");
+            return;
+        }
+    }
+}
+
+static void minify_string(char** input, char** output) {
+    (*output)[0] = (*input)[0];
+    *input += static_strlen("\"");
+    *output += static_strlen("\"");
+
+    for(; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+        (*output)[0] = (*input)[0];
+
+        if((*input)[0] == '\"') {
+            (*output)[0] = '\"';
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+            return;
+        } else if(((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+            (*output)[1] = (*input)[1];
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+        }
+    }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char* json) {
+    char* into = json;
+
+    if(json == NULL) {
+        return;
+    }
+
+    while(json[0] != '\0') {
+        switch(json[0]) {
+        case ' ':
+        case '\t':
+        case '\r':
+        case '\n':
+            json++;
+            break;
+
+        case '/':
+            if(json[1] == '/') {
+                skip_oneline_comment(&json);
+            } else if(json[1] == '*') {
+                skip_multiline_comment(&json);
+            } else {
+                json++;
+            }
+            break;
+
+        case '\"':
+            minify_string(&json, (char**)&into);
+            break;
+
+        default:
+            into[0] = json[0];
+            json++;
+            into++;
+        }
+    }
+
+    /* and null-terminate. */
+    *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xff) == cJSON_True;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item) {
+    if(item == NULL) {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive) {
+    if((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) {
+        return false;
+    }
+
+    /* check if type is valid */
+    switch(a->type & 0xFF) {
+    case cJSON_False:
+    case cJSON_True:
+    case cJSON_NULL:
+    case cJSON_Number:
+    case cJSON_String:
+    case cJSON_Raw:
+    case cJSON_Array:
+    case cJSON_Object:
+        break;
+
+    default:
+        return false;
+    }
+
+    /* identical objects are equal */
+    if(a == b) {
+        return true;
+    }
+
+    switch(a->type & 0xFF) {
+    /* in these cases and equal type is enough */
+    case cJSON_False:
+    case cJSON_True:
+    case cJSON_NULL:
+        return true;
+
+    case cJSON_Number:
+        if(compare_double(a->valuedouble, b->valuedouble)) {
+            return true;
+        }
+        return false;
+
+    case cJSON_String:
+    case cJSON_Raw:
+        if((a->valuestring == NULL) || (b->valuestring == NULL)) {
+            return false;
+        }
+        if(strcmp(a->valuestring, b->valuestring) == 0) {
+            return true;
+        }
+
+        return false;
+
+    case cJSON_Array: {
+        cJSON* a_element = a->child;
+        cJSON* b_element = b->child;
+
+        for(; (a_element != NULL) && (b_element != NULL);) {
+            if(!cJSON_Compare(a_element, b_element, case_sensitive)) {
+                return false;
+            }
+
+            a_element = a_element->next;
+            b_element = b_element->next;
+        }
+
+        /* one of the arrays is longer than the other */
+        if(a_element != b_element) {
+            return false;
+        }
+
+        return true;
+    }
+
+    case cJSON_Object: {
+        cJSON* a_element = NULL;
+        cJSON* b_element = NULL;
+        cJSON_ArrayForEach(a_element, a) {
+            /* TODO This has O(n^2) runtime, which is horrible! */
+            b_element = get_object_item(b, a_element->string, case_sensitive);
+            if(b_element == NULL) {
+                return false;
+            }
+
+            if(!cJSON_Compare(a_element, b_element, case_sensitive)) {
+                return false;
+            }
+        }
+
+        /* doing this twice, once on a and b to prevent true comparison if a subset of b
+             * TODO: Do this the proper way, this is just a fix for now */
+        cJSON_ArrayForEach(b_element, b) {
+            a_element = get_object_item(a, b_element->string, case_sensitive);
+            if(a_element == NULL) {
+                return false;
+            }
+
+            if(!cJSON_Compare(b_element, a_element, case_sensitive)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    default:
+        return false;
+    }
+}
+
+CJSON_PUBLIC(void*) cJSON_malloc(size_t size) {
+    return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void* object) {
+    global_hooks.deallocate(object);
+}

+ 321 - 0
cJSON.h

@@ -0,0 +1,321 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(__WINDOWS__) && \
+    (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \
+    !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type) type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if(defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \
+    defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 15
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False (1 << 0)
+#define cJSON_True (1 << 1)
+#define cJSON_NULL (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON {
+    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+    struct cJSON* next;
+    struct cJSON* prev;
+    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+    struct cJSON* child;
+
+    /* The type of the item, as above. */
+    int type;
+
+    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
+    char* valuestring;
+    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+    int valueint;
+    /* The item's number, if type==cJSON_Number */
+    double valuedouble;
+
+    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+    char* string;
+} cJSON;
+
+typedef struct cJSON_Hooks {
+    /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+    void*(CJSON_CDECL* malloc_fn)(size_t sz);
+    void(CJSON_CDECL* free_fn)(void* ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value);
+CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON*)
+cJSON_ParseWithOpts(
+    const char* value,
+    const char** return_parse_end,
+    cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON*)
+cJSON_ParseWithLengthOpts(
+    const char* value,
+    size_t buffer_length,
+    const char** return_parse_end,
+    cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON* item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string);
+CJSON_PUBLIC(cJSON*)
+cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string);
+/* raw json */
+CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw);
+CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child);
+CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count);
+CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count);
+CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count);
+CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item);
+CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which);
+CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string);
+CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_InsertItemInArray(
+    cJSON* array,
+    int which,
+    cJSON* newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant, 
+ * but should point to a readable and writable address area. */
+CJSON_PUBLIC(void) cJSON_Minify(char* json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name);
+CJSON_PUBLIC(cJSON*)
+cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*)
+cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number);
+CJSON_PUBLIC(cJSON*)
+cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string);
+CJSON_PUBLIC(cJSON*)
+cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) \
+    ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number);
+#define cJSON_SetNumberValue(object, number) \
+    ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring);
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array)                                  \
+    for(element = (array != NULL) ? (array)->child : NULL; element != NULL; \
+        element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void*) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void* object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

BIN
docs/images/captive-portal-6.png


BIN
docs/images/flipper-zero-flat-1.png


BIN
docs/images/flipper-zero-flat-2.png


BIN
docs/images/flipper-zero-flat-3.png


BIN
docs/images/gbp-6.png


BIN
docs/images/transfer-7.gif


BIN
docs/images/wifi-4.png


BIN
docs/images/wifi-5.png


BIN
docs/psd/gb_link_camera.psd


BIN
icons/boilerplate_10px.png


BIN
icons/malveke_67x49.png


+ 234 - 0
link_camera.c

@@ -0,0 +1,234 @@
+#include "link_camera.h"
+#include <gui/elements.h>
+#include <malveke_gb_link_camera_icons.h>
+// #include <u8g2_glue.h>
+
+QRCode qrcode;
+uint8_t qrcodeData[((((4 * 8 + 17) * (4 * 8 + 17)) + 7) / 8)];
+
+
+static uint32_t link_camera_exit(void* context) {
+    UNUSED(context);
+    return VIEW_NONE;
+}
+
+static void view_draw_callback(Canvas* canvas, void* _model) {
+    LinkCameraModel* model = _model;
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontPrimary);
+    
+
+    if(model->connected) {
+        // canvas_draw_str_aligned(canvas, 128/2, 64/3, AlignCenter, AlignCenter, "Connected");
+        // canvas_draw_str_aligned(canvas, 128/2, 64/2, AlignCenter, AlignCenter, model->ip);
+        /*
+        char ssid[64]; // Incrementa el tamaño a 64
+        char password[64]; // Incrementa el tamaño a 64
+        char qrstr[150]; // Deja qrstr con el tamaño actual
+        snprintf(ssid, sizeof(ssid), "%s", "[MALVEKE] Flipper GB Camera");
+        snprintf(password, sizeof(password), "%s", "12345678");
+        snprintf(qrstr, sizeof(qrstr), "WIFI:S:%s;T:WPA2;P:%s;;", ssid, password);
+        qrcode_initText(&qrcode, qrcodeData, 2, 0, qrstr);
+        // canvas_draw_frame(canvas, 63, 63, 0, 0);
+
+        for (uint8_t y = 0; y < qrcode.size; y++) {
+            // Cada módulo horizontal
+            for (uint8_t x = 0; x < qrcode.size; x++) {
+                if (qrcode_getModule(&qrcode, x, y)) {
+                    if(qrcode_getModule(&qrcode, x, y)) {
+                        // canvas_draw_dot(canvas, x, y);
+                        int scale = 2;
+                        // // Dibuja cada punto dos veces para escalar al doble
+                        canvas_draw_dot(canvas, (x * scale) + 2, (y * scale) + 4);
+                        canvas_draw_dot(canvas, (x * scale + 1) + 2, (y * scale) + 4);
+                        canvas_draw_dot(canvas, (x * scale) + 2, (y * scale + 1) + 4);
+                        canvas_draw_dot(canvas, (x * scale + 1) + 2, (y * scale + 1) + 4);
+                    }
+                }
+            }
+        }
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, 62, 11, "SSID");
+        canvas_set_font(canvas, FontSecondary);
+        // u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr);
+        // canvas_set_font(canvas, FontBatteryPercent);
+        canvas_draw_str(canvas, 62, 20, "[MALVEKE] Flipper GB Camera");
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, 62, 30, "Password");
+        canvas_set_font(canvas, FontSecondary);
+        // canvas_set_font(canvas, u8g2_font_5x7_tf);
+        canvas_draw_str(canvas, 62, 40, "12345678");
+
+        */
+       canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, 2, 11, "SSID");
+        canvas_set_font(canvas, FontSecondary);
+        // u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr);
+        // canvas_set_font(canvas, FontBatteryPercent);
+        canvas_draw_str(canvas, 2, 20, "[MALVEKE] Flipper GB Cam");
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, 2, 30, "Password");
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str(canvas, 2, 40, "12345678");
+
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, 2, 50, "Host");
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str(canvas, 2, 60, model->ip);
+
+
+    } else {
+        canvas_draw_icon(canvas, 60, 7, &I_malveke_67x49);
+        canvas_set_font(canvas, FontSecondary);
+
+        canvas_draw_str(canvas, 4, 25, "Connect");
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, 4, 35, "MALVEKE");
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str(canvas, 4, 44, "into Flipper");
+        elements_button_center(canvas, "Ok"); 
+    }
+    
+
+    if (!model->initialized){
+        canvas_clear(canvas);
+        canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48);
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str(canvas, 8, 12, "Waiting MALVEKE Board...");
+        // canvas_draw_str(canvas, 20, 24, "VCC - 3V3/5V");
+        // canvas_draw_str(canvas, 20, 34, "GND - GND");
+        // canvas_draw_str(canvas, 20, 44, "U0R - TX");
+        // canvas_draw_str(canvas, 20, 54, "U0T - RX");
+    }
+}
+static bool view_input_callback(InputEvent* event, void* context) {
+    LinkCameraApp* instance = context;
+    UNUSED(instance);
+    if (event->type == InputTypePress){
+        if (event->key == InputKeyOk){
+            uart_tx((uint8_t*)("c"), 1);
+            uart_tx((uint8_t*)("\n"), 1);
+        }
+    }
+    return false;
+}
+static void link_camera_free(LinkCameraApp* app) {
+    furi_assert(app);
+    // Free views
+    view_dispatcher_remove_view(app->view_dispatcher, 0);
+    view_free(app->view);
+    view_dispatcher_free(app->view_dispatcher);
+    // Close gui record
+    furi_record_close(RECORD_GUI);
+    furi_record_close(RECORD_STORAGE);
+    app->gui = NULL;
+    // Free rest
+    free(app);
+}
+void handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
+    furi_assert(context);
+    UNUSED(len);
+    // UNUSED(buf);
+    LinkCameraApp* instance = (LinkCameraApp*)context;
+    
+    // FURI_LOG_I("UART", "[in]: %s", (char*)buf);
+    with_view_model(
+        instance->view,
+        LinkCameraModel * model,
+        {
+            cJSON* json = cJSON_Parse((char*)buf);
+            if(json == NULL) {
+                
+            } else {
+                cJSON* type = cJSON_GetObjectItemCaseSensitive(json, "type");
+                if(cJSON_IsString(type) && (type->valuestring != NULL)) {
+                    model->event_type = strdup(type->valuestring);
+                } else {
+                    model->event_type = "None";
+                }
+
+                if (strcmp(model->event_type, "connected") == 0) {
+                    cJSON* ip = cJSON_GetObjectItemCaseSensitive(json, "ip");
+                    if(cJSON_IsString(ip) && (ip->valuestring != NULL)) {
+                        model->ip = strdup(ip->valuestring);
+                    } else {
+                        model->ip = "0.0.0.0";
+                    }
+                    cJSON* ssid = cJSON_GetObjectItemCaseSensitive(json, "ssid");
+                    if(cJSON_IsString(ssid) && (ssid->valuestring != NULL)) {
+                        model->ssid = strdup(ssid->valuestring);
+                    } else {
+                        model->ssid = " - ";
+                    }
+                    cJSON* password = cJSON_GetObjectItemCaseSensitive(json, "password");
+                    if(cJSON_IsString(password) && (password->valuestring != NULL)) {
+                        model->password = strdup(password->valuestring);
+                    } else {
+                        model->password = "******";
+                    }
+                    model->connected = true;
+                }
+            }
+            
+        },
+        true);
+}
+static LinkCameraApp* link_camera_alloc() {
+    LinkCameraApp* app = malloc(sizeof(LinkCameraApp));
+    // Gui
+    app->gui = furi_record_open(RECORD_GUI);
+    app->storage = furi_record_open(RECORD_STORAGE);
+
+    app->uart = usart_init(app);
+    uart_set_handle_rx_data_cb(app->uart, handle_rx_data_cb);
+
+    // View dispatcher
+    app->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_enable_queue(app->view_dispatcher);
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+    // Views
+    app->view = view_alloc();
+    view_set_context(app->view, app);
+    view_set_draw_callback(app->view, view_draw_callback);
+    view_set_input_callback(app->view, view_input_callback);
+    view_allocate_model(app->view, ViewModelTypeLocking, sizeof(LinkCameraModel));
+
+    
+
+    view_set_previous_callback(app->view, link_camera_exit);
+    view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
+    view_dispatcher_switch_to_view(app->view_dispatcher, 0);
+
+    furi_hal_power_disable_external_3_3v();
+    furi_delay_ms(100);
+    furi_hal_power_enable_external_3_3v();
+    furi_delay_ms(200);
+
+    with_view_model(
+        app->view,
+        LinkCameraModel * model,
+        {
+            model->initialized = true;
+        },
+        true);
+    uart_tx((uint8_t*)("c"), 1);
+    uart_tx((uint8_t*)("\n"), 1);
+
+    
+
+    return app;
+}
+
+int32_t link_camera_app(void* p) {
+    UNUSED(p);
+
+    LinkCameraApp* app = link_camera_alloc();
+    view_dispatcher_run(app->view_dispatcher);
+    link_camera_free(app);
+    
+    // furi_hal_power_disable_otg();
+
+    return 0;
+}

+ 98 - 0
link_camera.h

@@ -0,0 +1,98 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include <gui/gui.h>
+#include <gui/icon_i.h>
+#include <storage/storage.h>
+#include <gui/view_dispatcher.h>
+#include "cJSON.h"
+#include "qrcode.h"
+#include "uart.h"
+
+typedef struct LinkCameraModel LinkCameraModel;
+
+typedef struct {
+    Gui* gui;
+    Storage* storage;
+    ViewDispatcher* view_dispatcher;
+    View* view;
+    Uart* uart;
+    QRCode* qrcode;
+    uint8_t set_version;
+} LinkCameraApp;
+
+struct LinkCameraModel {
+    bool initialized;
+    bool connected;
+    char* ip;
+    char* event_type;
+    char* ssid;
+    char* password;
+};
+
+typedef enum {
+    WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
+    WorkerEventStop = (1 << 1),
+    WorkerEventRx = (1 << 2),
+} WorkerEventFlags;
+
+#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
+
+
+const uint8_t _I_DolphinCommon_56x48_0[] = {0x01,0x00,0xdf,0x00,0x00,0x1f,0xfe,0x0e,0x05,0x3f,0x04,0x06,0x78,0x06,0x30,0x20,0xf8,0x00,0xc6,0x12,0x1c,0x04,0x0c,0x0a,0x38,0x08,0x08,0x0c,0x60,0xc0,0x21,0xe0,0x04,0x0a,0x18,0x02,0x1b,0x00,0x18,0xa3,0x00,0x21,0x90,0x01,0x8a,0x20,0x02,0x19,0x80,0x18,0x80,0x64,0x09,0x20,0x89,0x81,0x8c,0x3e,0x41,0xe2,0x80,0x50,0x00,0x43,0x08,0x01,0x0c,0xfc,0x68,0x40,0x61,0xc0,0x50,0x30,0x00,0x63,0xa0,0x7f,0x80,0xc4,0x41,0x19,0x07,0xff,0x02,0x06,0x18,0x24,0x03,0x41,0xf3,0x2b,0x10,0x19,0x38,0x10,0x30,0x31,0x7f,0xe0,0x34,0x08,0x30,0x19,0x60,0x80,0x65,0x86,0x0a,0x4c,0x0c,0x30,0x81,0xb9,0x41,0xa0,0x54,0x08,0xc7,0xe2,0x06,0x8a,0x18,0x25,0x02,0x21,0x0f,0x19,0x88,0xd8,0x6e,0x1b,0x01,0xd1,0x1b,0x86,0x39,0x66,0x3a,0xa4,0x1a,0x50,0x06,0x48,0x18,0x18,0xd0,0x03,0x01,0x41,0x98,0xcc,0x60,0x39,0x01,0x49,0x2d,0x06,0x03,0x50,0xf8,0x40,0x3e,0x02,0xc1,0x82,0x86,0xc7,0xfe,0x0f,0x28,0x2c,0x91,0xd2,0x90,0x9a,0x18,0x19,0x3e,0x6d,0x73,0x12,0x16,0x00,0x32,0x49,0x72,0xc0,0x7e,0x5d,0x44,0xba,0x2c,0x08,0xa4,0xc8,0x82,0x06,0x17,0xe0,0x81,0x90,0x2a,0x40,0x61,0xe1,0xa2,0x44,0x0c,0x76,0x2b,0xe8,0x89,0x26,0x43,0x83,0x31,0x8c,0x78,0x0c,0xb0,0x48,0x10,0x1a,0xe0,0x00,0x63,};
+const uint8_t* const _I_DolphinCommon_56x48[] = {_I_DolphinCommon_56x48_0};
+const Icon I_DolphinCommon_56x48 = {.width=56,.height=48,.frame_count=1,.frame_rate=0,.frames=_I_DolphinCommon_56x48};
+
+
+
+const uint8_t u8g2_font_5x7_tf[1612] = 
+  "\277\0\2\2\3\3\3\4\4\5\7\0\377\6\377\6\0\1\12\2\26\6/ \5\0\275\1!\6\261\261"
+  "\31)\42\7[\267IV\0#\12-\261\253\206\252\206\252\0$\11-\261[\365Ni\1%\10\64\261"
+  "\311\261w\0&\11,\261\213)V\61\5'\5\231\267\31(\7r\261S\315\0)\10r\261\211\251R"
+  "\0*\7k\261I\325j+\12-\261\315(\16\231Q\4,\7[\257S%\0-\6\14\265\31\1."
+  "\6R\261\31\1/\7$\263\217m\0\60\10s\261\253\134\25\0\61\7s\261K\262\65\62\11\64\261S"
+  "\61\307r\4\63\12\64\261\31\71i$\223\2\64\12\64\261\215\252\32\61'\0\65\12\64\261\31z#\231"
+  "\24\0\66\12\64\261SyE\231\24\0\67\12\64\261\31\71\346\230#\0\70\12\64\261S\61\251(\223\2"
+  "\71\12\64\261SQ\246\235\24\0:\7j\261\31q\4;\10\63\257\263\221*\1<\7k\261Mu\1"
+  "=\10\34\263\31\31\215\0>\7k\261\311U\11\77\11s\261k\246\14\23\0@\11\64\261SQ\335H"
+  "\1A\11\64\261SQ\216)\3B\12\64\261Yq\244(G\2C\11\64\261SQ\227I\1D\11\64"
+  "\261Y\321\71\22\0E\11\64\261\31z\345<\2F\10\64\261\31z\345\32G\11\64\261SQ\247\231\6"
+  "H\10\64\261\211rL\63I\7s\261Y\261\65J\10\64\261o\313\244\0K\12\64\261\211*I\231\312"
+  "\0L\7\64\261\311\335#M\11\64\261\211\343\210f\0N\10\64\261\211k\251\63O\11\64\261S\321\231"
+  "\24\0P\12\64\261YQ\216\224\63\0Q\12<\257S\321\134I\243\0R\11\64\261YQ\216\324\14S"
+  "\12\64\261S\61eT&\5T\7s\261Y\261\13U\10\64\261\211\236I\1V\11\64\261\211\316$\25"
+  "\0W\11\64\261\211\346\70b\0X\12\64\261\211\62I\25e\0Y\10s\261IVY\1Z\11\64\261"
+  "\31\71\266G\0[\7s\261\31\261\71\134\11$\263\311(\243\214\2]\7s\261\231\315\21^\5S\271"
+  "k_\6\14\261\31\1`\6R\271\211\1a\10$\261\33Q\251\2b\12\64\261\311yE\71\22\0c"
+  "\6#\261\233Yd\10\64\261\257F\224ie\10$\261Sid\5f\11\64\261\255\312\231#\0g\11"
+  ",\257\33\61\251\214\6h\10\64\261\311yE\63i\10s\261\313HV\3j\11{\257\315\260T\25\0"
+  "k\11\64\261\311U\222\251\14l\7s\261\221]\3m\10$\261IiH\31n\7$\261Y\321\14o"
+  "\10$\261SQ&\5p\11,\257YQ\216\224\1q\10,\257\33Q\246\35r\10$\261YQg\0"
+  "s\10$\261\33\32\15\5t\11\64\261\313q\346\214\4u\7$\261\211f\32v\7c\261IV\5w"
+  "\7$\261\211r\34x\10$\261\211I\252\30y\11,\257\211\62\225%\0z\10$\261\31\261\34\1{"
+  "\10s\261MI\326\1|\5\261\261\71}\11s\261\311Q\305\24\1~\7\24\271K*\1\240\5\0\275"
+  "\1\241\6\261\261I#\242\11\64\257\215#\65g\2\243\10,\261UqV\2\244\12-\261\311i\246\270"
+  "r\0\245\11s\261I\252Z\61\1\246\6\251\261Q\2\247\10{\257\233\252\222\13\250\6K\273I\1\251"
+  "\13=\257[Y\245\251\222N\13\252\6\33\267[I\253\7\35\263\213\262\1\254\6\24\263\31\71\255\5K"
+  "\265\31\256\13=\257[y\244\71uZ\0\257\6\14\273\31\1\260\6[\267\353\2\261\13\65\261\315(\16"
+  "\231Q\34\2\262\6b\265Q\6\263\6b\265\31i\264\6R\271S\0\265\10,\257\211\346H\31\266\10"
+  "\64\261\33j\365\3\267\6R\265\31\1\270\6R\257S\0\271\7c\265K\62\15\272\6\33\267\353\2\273"
+  "\10\35\263\211\245L\0\274\11<\257\311\315\250v\0\275\11<\257\311-\35\263\0\276\12<\257\221:\252"
+  "\250v\0\277\11s\261\313\60\305T\1\300\11\64\261SQ\216)\3\301\11\64\261SQ\216)\3\302\11"
+  "\64\261SQ\216)\3\303\11\64\261SQ\216)\3\304\12\64\261\211I\305\61e\0\305\11\64\261\223*"
+  "\216)\3\306\11\64\261\33\251\32\252%\307\12<\257SQ\227IF\0\310\11\64\261\31z\345<\2\311"
+  "\11\64\261\31z\345<\2\312\11\64\261\31z\345<\2\313\11\64\261\31z\345<\2\314\7s\261Y\261"
+  "\65\315\7s\261Y\261\65\316\7s\261Y\261\65\317\7s\261Y\261\65\320\11\64\261\231iu\215\4\321"
+  "\10\64\261Is\251\63\322\11\64\261S\321\231\24\0\323\11\64\261S\321\231\24\0\324\11\64\261S\321\231"
+  "\24\0\325\11\64\261S\321\231\24\0\326\12\64\261\211IE\63)\0\327\10$\261\211I\252\30\330\11\64"
+  "\261\33\351HG\2\331\10\64\261\211\236I\1\332\10\64\261\211\236I\1\333\10\64\261\211\236I\1\334\12"
+  "\64\261\211\31E\63)\0\335\10s\261IVY\1\336\12\64\261\311+\216\224\63\0\337\11\64\261SQ"
+  "\225V\2\340\12\64\261\313(\216\250T\1\341\11\64\261\255GT\252\0\342\11\64\261\255\322\210J\25\343"
+  "\12\64\261K*\216\250T\1\344\11\64\261\253\341\210J\25\345\11\64\261\223rD\245\12\346\10$\261\33"
+  "i\305\1\347\10k\257\233Y%\0\350\12\64\261\313(\252\64\262\2\351\11\64\261\255U\32Y\1\352\12"
+  "\64\261\213)\252\64\262\2\353\12\64\261I\31\253\64\262\2\354\10s\261\311IV\3\355\7s\261+e"
+  "\65\356\7s\261\253f\65\357\10s\261IYV\3\360\12\64\261\313\310\212\62)\0\361\11\64\261K*"
+  "\255h\6\362\12\64\261\313(\252(\223\2\363\11\64\261\255U\224I\1\364\12\64\261\323XE\231\24\0"
+  "\365\12\64\261K*\252(\223\2\366\12\64\261\253\241\212\62)\0\367\11,\261\323pd\250\0\370\11$"
+  "\261\33i\244\221\0\371\11\64\261\313\250\64\323\0\372\7\64\261m\232i\373\10\64\261\323\60\232i\374\11"
+  "\64\261\253Q\64\323\0\375\11<\257m\312T\226\0\376\12\64\257\311+\312\221\62\0\377\12<\257\253Q"
+  "\224\251,\1\0\0\0\4\377\377\0";

+ 876 - 0
qrcode.c

@@ -0,0 +1,876 @@
+/**
+ * The MIT License (MIT)
+ *
+ * This library is written and maintained by Richard Moore.
+ * Major parts were derived from Project Nayuki's library.
+ *
+ * Copyright (c) 2017 Richard Moore     (https://github.com/ricmoo/QRCode)
+ * Copyright (c) 2017 Project Nayuki    (https://www.nayuki.io/page/qr-code-generator-library)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ *  Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
+ *  heavily inspired and compared against.
+ *
+ *  See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
+ */
+
+#include "qrcode.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// #pragma mark - Error Correction Lookup tables
+
+#if LOCK_VERSION == 0
+
+static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = {
+    // 1,  2,  3,  4,  5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,   25,   26,   27,   28,   29,   30,   31,   32,   33,   34,   35,   36,   37,   38,   39,   40    Error correction level
+    { 10, 16, 26, 36, 48,  64,  72,  88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560,  588,  644,  700,  728,  784,  812,  868,  924,  980, 1036, 1064, 1120, 1204, 1260, 1316, 1372},  // Medium
+    {  7, 10, 15, 20, 26,  36,  40,  48,  60,  72,  80,  96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300,  312,  336,  360,  390,  420,  450,  480,  510,  540,  570,  570,  600,  630,  660,  720,  750},  // Low
+    { 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430},  // High
+    { 13, 22, 36, 52, 72,  96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810,  870,  952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040},  // Quartile
+};
+
+static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = {
+    // Version: (note that index 0 is for padding, and is set to an illegal value)
+    // 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
+    {  1, 1, 1, 2, 2, 4, 4, 4, 5, 5,  5,  8,  9,  9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49},  // Medium
+    {  1, 1, 1, 1, 1, 2, 2, 2, 2, 4,  4,  4,  4,  4,  6,  6,  6,  6,  7,  8,  8,  9,  9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25},  // Low
+    {  1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81},  // High
+    {  1, 1, 2, 2, 4, 4, 6, 6, 8, 8,  8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68},  // Quartile
+};
+
+static const uint16_t NUM_RAW_DATA_MODULES[40] = {
+    //  1,   2,   3,   4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,   15,   16,   17,
+      208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523,
+    //   18,   19,   20,   21,    22,    23,    24,    25,   26,    27,     28,    29,    30,    31,
+       7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587,
+    //    32,    33,    34,    35,    36,    37,    38,    39,    40
+       19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648
+};
+
+// @TODO: Put other LOCK_VERSIONS here
+#elif LOCK_VERSION == 3
+
+static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = {
+    26, 15, 44, 36
+};
+
+static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = {
+    1, 1, 2, 2
+};
+
+static const uint16_t NUM_RAW_DATA_MODULES = 567;
+
+#else
+
+#error Unsupported LOCK_VERSION (add it...)
+
+#endif
+
+
+static int max(int a, int b) {
+    if (a > b) { return a; }
+    return b;
+}
+
+/*
+static int abs(int value) {
+    if (value < 0) { return -value; }
+    return value;
+}
+*/
+
+
+// #pragma mark - Mode testing and conversion
+
+static int8_t getAlphanumeric(char c) {
+    
+    if (c >= '0' && c <= '9') { return (c - '0'); }
+    if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); }
+    
+    switch (c) {
+        case ' ': return 36;
+        case '$': return 37;
+        case '%': return 38;
+        case '*': return 39;
+        case '+': return 40;
+        case '-': return 41;
+        case '.': return 42;
+        case '/': return 43;
+        case ':': return 44;
+    }
+    
+    return -1;
+}
+
+static bool isAlphanumeric(const char *text, uint16_t length) {
+    while (length != 0) {
+        if (getAlphanumeric(text[--length]) == -1) { return false; }
+    }
+    return true;
+}
+
+
+static bool isNumeric(const char *text, uint16_t length) {
+    while (length != 0) {
+        char c = text[--length];
+        if (c < '0' || c > '9') { return false; }
+    }
+    return true;
+}
+
+
+// #pragma mark - Counting
+
+// We store the following tightly packed (less 8) in modeInfo
+//               <=9  <=26  <= 40
+// NUMERIC      ( 10,   12,    14);
+// ALPHANUMERIC (  9,   11,    13);
+// BYTE         (  8,   16,    16);
+static char getModeBits(uint8_t version, uint8_t mode) {
+    // Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits
+    // hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2))
+    unsigned int modeInfo = 0x7bbb80a;
+    
+#if LOCK_VERSION == 0 || LOCK_VERSION > 9
+    if (version > 9) { modeInfo >>= 9; }
+#endif
+    
+#if LOCK_VERSION == 0 || LOCK_VERSION > 26
+    if (version > 26) { modeInfo >>= 9; }
+#endif
+    
+    char result = 8 + ((modeInfo >> (3 * mode)) & 0x07);
+    if (result == 15) { result = 16; }
+    
+    return result;
+}
+
+
+// #pragma mark - BitBucket
+
+typedef struct BitBucket {
+    uint32_t bitOffsetOrWidth;
+    uint16_t capacityBytes;
+    uint8_t *data;
+} BitBucket;
+
+/*
+void bb_dump(BitBucket *bitBuffer) {
+    printf("Buffer: ");
+    for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) {
+        printf("%02x", bitBuffer->data[i]);
+        if ((i % 4) == 3) { printf(" "); }
+    }
+    printf("\n");
+}
+*/
+
+static uint16_t bb_getGridSizeBytes(uint8_t size) {
+    return (((size * size) + 7) / 8);
+}
+
+static uint16_t bb_getBufferSizeBytes(uint32_t bits) {
+    return ((bits + 7) / 8);
+}
+
+static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) {
+    bitBuffer->bitOffsetOrWidth = 0;
+    bitBuffer->capacityBytes = capacityBytes;
+    bitBuffer->data = data;
+    
+    memset(data, 0, bitBuffer->capacityBytes);
+}
+
+static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) {
+    bitGrid->bitOffsetOrWidth = size;
+    bitGrid->capacityBytes = bb_getGridSizeBytes(size);
+    bitGrid->data = data;
+
+    memset(data, 0, bitGrid->capacityBytes);
+}
+
+static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) {
+    uint32_t offset = bitBuffer->bitOffsetOrWidth;
+    for (int8_t i = length - 1; i >= 0; i--, offset++) {
+        bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
+    }
+    bitBuffer->bitOffsetOrWidth = offset;
+}
+/*
+void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) {
+    for (int8_t i = length - 1; i >= 0; i--, offset++) {
+        bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
+    }
+}
+*/
+static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) {
+    uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
+    uint8_t mask = 1 << (7 - (offset & 0x07));
+    if (on) {
+        bitGrid->data[offset >> 3] |= mask;
+    } else {
+        bitGrid->data[offset >> 3] &= ~mask;
+    }
+}
+
+static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) {
+    uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
+    uint8_t mask = 1 << (7 - (offset & 0x07));
+    bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0);
+    if (on ^ invert) {
+        bitGrid->data[offset >> 3] |= mask;
+    } else {
+        bitGrid->data[offset >> 3] &= ~mask;
+    }
+}
+
+static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) {
+    uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
+    return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
+}
+
+
+// #pragma mark - Drawing Patterns
+
+// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical
+// properties, calling applyMask(m) twice with the same value is equivalent to no change at all.
+// This means it is possible to apply a mask, undo it, and try another mask. Note that a final
+// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).
+static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) {
+    uint8_t size = modules->bitOffsetOrWidth;
+    
+    for (uint8_t y = 0; y < size; y++) {
+        for (uint8_t x = 0; x < size; x++) {
+            if (bb_getBit(isFunction, x, y)) { continue; }
+            
+            bool invert = 0;
+            switch (mask) {
+                case 0:  invert = (x + y) % 2 == 0;                    break;
+                case 1:  invert = y % 2 == 0;                          break;
+                case 2:  invert = x % 3 == 0;                          break;
+                case 3:  invert = (x + y) % 3 == 0;                    break;
+                case 4:  invert = (x / 3 + y / 2) % 2 == 0;            break;
+                case 5:  invert = x * y % 2 + x * y % 3 == 0;          break;
+                case 6:  invert = (x * y % 2 + x * y % 3) % 2 == 0;    break;
+                case 7:  invert = ((x + y) % 2 + x * y % 3) % 2 == 0;  break;
+            }
+            bb_invertBit(modules, x, y, invert);
+        }
+    }
+}
+
+static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) {
+    bb_setBit(modules, x, y, on);
+    bb_setBit(isFunction, x, y, true);
+}
+
+// Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).
+static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
+    uint8_t size = modules->bitOffsetOrWidth;
+
+    for (int8_t i = -4; i <= 4; i++) {
+        for (int8_t j = -4; j <= 4; j++) {
+            uint8_t dist = max(abs(i), abs(j));  // Chebyshev/infinity norm
+            int16_t xx = x + j, yy = y + i;
+            if (0 <= xx && xx < size && 0 <= yy && yy < size) {
+                setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4);
+            }
+        }
+    }
+}
+
+// Draws a 5*5 alignment pattern, with the center module at (x, y).
+static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
+    for (int8_t i = -2; i <= 2; i++) {
+        for (int8_t j = -2; j <= 2; j++) {
+            setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1);
+        }
+    }
+}
+
+// Draws two copies of the format bits (with its own error correction code)
+// based on the given mask and this object's error correction level field.
+static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) {
+    
+    uint8_t size = modules->bitOffsetOrWidth;
+
+    // Calculate error correction code and pack bits
+    uint32_t data = ecc << 3 | mask;  // errCorrLvl is uint2, mask is uint3
+    uint32_t rem = data;
+    for (int i = 0; i < 10; i++) {
+        rem = (rem << 1) ^ ((rem >> 9) * 0x537);
+    }
+    
+    data = data << 10 | rem;
+    data ^= 0x5412;  // uint15
+    
+    // Draw first copy
+    for (uint8_t i = 0; i <= 5; i++) {
+        setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0);
+    }
+    
+    setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0);
+    setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0);
+    setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0);
+    
+    for (int8_t i = 9; i < 15; i++) {
+        setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0);
+    }
+    
+    // Draw second copy
+    for (int8_t i = 0; i <= 7; i++) {
+        setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0);
+    }
+    
+    for (int8_t i = 8; i < 15; i++) {
+        setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0);
+    }
+    
+    setFunctionModule(modules, isFunction, 8, size - 8, true);
+}
+
+
+// Draws two copies of the version bits (with its own error correction code),
+// based on this object's version field (which only has an effect for 7 <= version <= 40).
+static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) {
+    
+    int8_t size = modules->bitOffsetOrWidth;
+
+#if LOCK_VERSION != 0 && LOCK_VERSION < 7
+    return;
+    
+#else
+    if (version < 7) { return; }
+    
+    // Calculate error correction code and pack bits
+    uint32_t rem = version;  // version is uint6, in the range [7, 40]
+    for (uint8_t i = 0; i < 12; i++) {
+        rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
+    }
+    
+    uint32_t data = version << 12 | rem;  // uint18
+    
+    // Draw two copies
+    for (uint8_t i = 0; i < 18; i++) {
+        bool bit = ((data >> i) & 1) != 0;
+        uint8_t a = size - 11 + i % 3, b = i / 3;
+        setFunctionModule(modules, isFunction, a, b, bit);
+        setFunctionModule(modules, isFunction, b, a, bit);
+    }
+    
+#endif
+}
+
+static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) {
+    
+    uint8_t size = modules->bitOffsetOrWidth;
+
+    // Draw the horizontal and vertical timing patterns
+    for (uint8_t i = 0; i < size; i++) {
+        setFunctionModule(modules, isFunction, 6, i, i % 2 == 0);
+        setFunctionModule(modules, isFunction, i, 6, i % 2 == 0);
+    }
+    
+    // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
+    drawFinderPattern(modules, isFunction, 3, 3);
+    drawFinderPattern(modules, isFunction, size - 4, 3);
+    drawFinderPattern(modules, isFunction, 3, size - 4);
+    
+#if LOCK_VERSION == 0 || LOCK_VERSION > 1
+
+    if (version > 1) {
+
+        // Draw the numerous alignment patterns
+        
+        uint8_t alignCount = version / 7 + 2;
+        uint8_t step;
+        if (version != 32) {
+            step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2;  // ceil((size - 13) / (2*numAlign - 2)) * 2
+        } else { // C-C-C-Combo breaker!
+            step = 26;
+        }
+        
+        uint8_t alignPositionIndex = alignCount - 1;
+        uint8_t alignPosition[alignCount];
+        
+        alignPosition[0] = 6;
+        
+        uint8_t size = version * 4 + 17;
+        for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) {
+            alignPosition[alignPositionIndex--] = pos;
+        }
+        
+        for (uint8_t i = 0; i < alignCount; i++) {
+            for (uint8_t j = 0; j < alignCount; j++) {
+                if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) {
+                    continue;  // Skip the three finder corners
+                } else {
+                    drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]);
+                }
+            }
+        }
+    }
+    
+#endif
+    
+    // Draw configuration data
+    drawFormatBits(modules, isFunction, ecc, 0);  // Dummy mask value; overwritten later in the constructor
+    drawVersion(modules, isFunction, version);
+}
+
+
+// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
+// data area of this QR Code symbol. Function modules need to be marked off before this is called.
+static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) {
+    
+    uint32_t bitLength = codewords->bitOffsetOrWidth;
+    uint8_t *data = codewords->data;
+    
+    uint8_t size = modules->bitOffsetOrWidth;
+    
+    // Bit index into the data
+    uint32_t i = 0;
+    
+    // Do the funny zigzag scan
+    for (int16_t right = size - 1; right >= 1; right -= 2) {  // Index of right column in each column pair
+        if (right == 6) { right = 5; }
+        
+        for (uint8_t vert = 0; vert < size; vert++) {  // Vertical counter
+            for (int j = 0; j < 2; j++) {
+                uint8_t x = right - j;  // Actual x coordinate
+                bool upwards = ((right & 2) == 0) ^ (x < 6);
+                uint8_t y = upwards ? size - 1 - vert : vert;  // Actual y coordinate
+                if (!bb_getBit(isFunction, x, y) && i < bitLength) {
+                    bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0);
+                    i++;
+                }
+                // If there are any remainder bits (0 to 7), they are already
+                // set to 0/false/white when the grid of modules was initialized
+            }
+        }
+    }
+}
+
+
+
+// #pragma mark - Penalty Calculation
+
+#define PENALTY_N1      3
+#define PENALTY_N2      3
+#define PENALTY_N3     40
+#define PENALTY_N4     10
+
+// Calculates and returns the penalty score based on state of this QR Code's current modules.
+// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
+// @TODO: This can be optimized by working with the bytes instead of bits.
+static uint32_t getPenaltyScore(BitBucket *modules) {
+    uint32_t result = 0;
+    
+    uint8_t size = modules->bitOffsetOrWidth;
+    
+    // Adjacent modules in row having same color
+    for (uint8_t y = 0; y < size; y++) {
+        
+        bool colorX = bb_getBit(modules, 0, y);
+        for (uint8_t x = 1, runX = 1; x < size; x++) {
+            bool cx = bb_getBit(modules, x, y);
+            if (cx != colorX) {
+                colorX = cx;
+                runX = 1;
+                
+            } else {
+                runX++;
+                if (runX == 5) {
+                    result += PENALTY_N1;
+                } else if (runX > 5) {
+                    result++;
+                }
+            }
+        }
+    }
+    
+    // Adjacent modules in column having same color
+    for (uint8_t x = 0; x < size; x++) {
+        bool colorY = bb_getBit(modules, x, 0);
+        for (uint8_t y = 1, runY = 1; y < size; y++) {
+            bool cy = bb_getBit(modules, x, y);
+            if (cy != colorY) {
+                colorY = cy;
+                runY = 1;
+            } else {
+                runY++;
+                if (runY == 5) {
+                    result += PENALTY_N1;
+                } else if (runY > 5) {
+                    result++;
+                }
+            }
+        }
+    }
+    
+    uint16_t black = 0;
+    for (uint8_t y = 0; y < size; y++) {
+        uint16_t bitsRow = 0, bitsCol = 0;
+        for (uint8_t x = 0; x < size; x++) {
+            bool color = bb_getBit(modules, x, y);
+
+            // 2*2 blocks of modules having same color
+            if (x > 0 && y > 0) {
+                bool colorUL = bb_getBit(modules, x - 1, y - 1);
+                bool colorUR = bb_getBit(modules, x, y - 1);
+                bool colorL = bb_getBit(modules, x - 1, y);
+                if (color == colorUL && color == colorUR && color == colorL) {
+                    result += PENALTY_N2;
+                }
+            }
+
+            // Finder-like pattern in rows and columns
+            bitsRow = ((bitsRow << 1) & 0x7FF) | color;
+            bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x);
+
+            // Needs 11 bits accumulated
+            if (x >= 10) {
+                if (bitsRow == 0x05D || bitsRow == 0x5D0) {
+                    result += PENALTY_N3;
+                }
+                if (bitsCol == 0x05D || bitsCol == 0x5D0) {
+                    result += PENALTY_N3;
+                }
+            }
+
+            // Balance of black and white modules
+            if (color) { black++; }
+        }
+    }
+
+    // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)%
+    uint16_t total = size * size;
+    for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) {
+        result += PENALTY_N4;
+    }
+    
+    return result;
+}
+
+
+// #pragma mark - Reed-Solomon Generator
+
+static uint8_t rs_multiply(uint8_t x, uint8_t y) {
+    // Russian peasant multiplication
+    // See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication
+    uint16_t z = 0;
+    for (int8_t i = 7; i >= 0; i--) {
+        z = (z << 1) ^ ((z >> 7) * 0x11D);
+        z ^= ((y >> i) & 1) * x;
+    }
+    return z;
+}
+
+static void rs_init(uint8_t degree, uint8_t *coeff) {
+    memset(coeff, 0, degree);
+    coeff[degree - 1] = 1;
+    
+    // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
+    // drop the highest term, and store the rest of the coefficients in order of descending powers.
+    // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
+    uint16_t root = 1;
+    for (uint8_t i = 0; i < degree; i++) {
+        // Multiply the current product by (x - r^i)
+        for (uint8_t j = 0; j < degree; j++) {
+            coeff[j] = rs_multiply(coeff[j], root);
+            if (j + 1 < degree) {
+                coeff[j] ^= coeff[j + 1];
+            }
+        }
+        root = (root << 1) ^ ((root >> 7) * 0x11D);  // Multiply by 0x02 mod GF(2^8/0x11D)
+    }
+}
+
+static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) {
+    // Compute the remainder by performing polynomial division
+    
+    //for (uint8_t i = 0; i < degree; i++) { result[] = 0; }
+    //memset(result, 0, degree);
+    
+    for (uint8_t i = 0; i < length; i++) {
+        uint8_t factor = data[i] ^ result[0];
+        for (uint8_t j = 1; j < degree; j++) {
+            result[(j - 1) * stride] = result[j * stride];
+        }
+        result[(degree - 1) * stride] = 0;
+        
+        for (uint8_t j = 0; j < degree; j++) {
+            result[j * stride] ^= rs_multiply(coeff[j], factor);
+        }
+    }
+}
+
+
+
+// #pragma mark - QrCode
+
+static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) {
+    int8_t mode = MODE_BYTE;
+    
+    if (isNumeric((char*)text, length)) {
+        mode = MODE_NUMERIC;
+        bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4);
+        bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC));
+
+        uint16_t accumData = 0;
+        uint8_t accumCount = 0;
+        for (uint16_t i = 0; i < length; i++) {
+            accumData = accumData * 10 + ((char)(text[i]) - '0');
+            accumCount++;
+            if (accumCount == 3) {
+                bb_appendBits(dataCodewords, accumData, 10);
+                accumData = 0;
+                accumCount = 0;
+            }
+        }
+        
+        // 1 or 2 digits remaining
+        if (accumCount > 0) {
+            bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1);
+        }
+        
+    } else if (isAlphanumeric((char*)text, length)) {
+        mode = MODE_ALPHANUMERIC;
+        bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4);
+        bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC));
+
+        uint16_t accumData = 0;
+        uint8_t accumCount = 0;
+        for (uint16_t i = 0; i  < length; i++) {
+            accumData = accumData * 45 + getAlphanumeric((char)(text[i]));
+            accumCount++;
+            if (accumCount == 2) {
+                bb_appendBits(dataCodewords, accumData, 11);
+                accumData = 0;
+                accumCount = 0;
+            }
+        }
+        
+        // 1 character remaining
+        if (accumCount > 0) {
+            bb_appendBits(dataCodewords, accumData, 6);
+        }
+        
+    } else {
+        bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4);
+        bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE));
+        for (uint16_t i = 0; i < length; i++) {
+            bb_appendBits(dataCodewords, (char)(text[i]), 8);
+        }
+    }
+    
+    //bb_setBits(dataCodewords, length, 4, getModeBits(version, mode));
+    
+    return mode;
+}
+
+static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) {
+    
+    // See: http://www.thonky.com/qr-code-tutorial/structure-final-message
+    
+#if LOCK_VERSION == 0
+    uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1];
+    uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1];
+    uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
+#else
+    uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc];
+    uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc];
+    uint16_t moduleCount = NUM_RAW_DATA_MODULES;
+#endif
+    
+    uint8_t blockEccLen = totalEcc / numBlocks;
+    uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks;
+    uint8_t shortBlockLen = moduleCount / 8 / numBlocks;
+    
+    uint8_t shortDataBlockLen = shortBlockLen - blockEccLen;
+    
+    uint8_t result[data->capacityBytes];
+    memset(result, 0, sizeof(result));
+    
+    uint8_t coeff[blockEccLen];
+    rs_init(blockEccLen, coeff);
+    
+    uint16_t offset = 0;
+    uint8_t *dataBytes = data->data;
+    
+    
+    // Interleave all short blocks
+    for (uint8_t i = 0; i < shortDataBlockLen; i++) {
+        uint16_t index = i;
+        uint8_t stride = shortDataBlockLen;
+        for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
+            result[offset++] = dataBytes[index];
+            
+#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
+            if (blockNum == numShortBlocks) { stride++; }
+#endif
+            index += stride;
+        }
+    }
+    
+    // Version less than 5 only have short blocks
+#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
+    {
+        // Interleave long blocks
+        uint16_t index = shortDataBlockLen * (numShortBlocks + 1);
+        uint8_t stride = shortDataBlockLen;
+        for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) {
+            result[offset++] = dataBytes[index];
+            
+            if (blockNum == 0) { stride++; }
+            index += stride;
+        }
+    }
+#endif
+    
+    // Add all ecc blocks, interleaved
+    uint8_t blockSize = shortDataBlockLen;
+    for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
+        
+#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
+        if (blockNum == numShortBlocks) { blockSize++; }
+#endif
+        rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks);
+        dataBytes += blockSize;
+    }
+    
+    memcpy(data->data, result, data->capacityBytes);
+    data->bitOffsetOrWidth = moduleCount;
+}
+
+// We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits)
+// The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc)
+static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0);
+
+
+// #pragma mark - Public QRCode functions
+
+uint16_t qrcode_getBufferSize(uint8_t version) {
+    return bb_getGridSizeBytes(4 * version + 17);
+}
+
+// @TODO: Return error if data is too big.
+int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) {
+    uint8_t size = version * 4 + 17;
+    qrcode->version = version;
+    qrcode->size = size;
+    qrcode->ecc = ecc;
+    qrcode->modules = modules;
+    
+    uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03;
+    
+#if LOCK_VERSION == 0
+    uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
+    uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1];
+#else
+    version = LOCK_VERSION;
+    uint16_t moduleCount = NUM_RAW_DATA_MODULES;
+    uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits];
+#endif
+    
+    struct BitBucket codewords;
+    uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)];
+    bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes));
+    
+    // Place the data code words into the buffer
+    int8_t mode = encodeDataCodewords(&codewords, data, length, version);
+    
+    if (mode < 0) { return -1; }
+    qrcode->mode = mode;
+    
+    // Add terminator and pad up to a byte if applicable
+    uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth;
+    if (padding > 4) { padding = 4; }
+    bb_appendBits(&codewords, 0, padding);
+    bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8);
+
+    // Pad with alternate bytes until data capacity is reached
+    for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) {
+        bb_appendBits(&codewords, padByte, 8);
+    }
+
+    BitBucket modulesGrid;
+    bb_initGrid(&modulesGrid, modules, size);
+    
+    BitBucket isFunctionGrid;
+    uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)];
+    bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size);
+    
+    // Draw function patterns, draw all codewords, do masking
+    drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits);
+    performErrorCorrection(version, eccFormatBits, &codewords);
+    drawCodewords(&modulesGrid, &isFunctionGrid, &codewords);
+    
+    // Find the best (lowest penalty) mask
+    uint8_t mask = 0;
+    int32_t minPenalty = INT32_MAX;
+    for (uint8_t i = 0; i < 8; i++) {
+        drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i);
+        applyMask(&modulesGrid, &isFunctionGrid, i);
+        int penalty = getPenaltyScore(&modulesGrid);
+        if (penalty < minPenalty) {
+            mask = i;
+            minPenalty = penalty;
+        }
+        applyMask(&modulesGrid, &isFunctionGrid, i);  // Undoes the mask due to XOR
+    }
+    
+    qrcode->mask = mask;
+    
+    // Overwrite old format bits
+    drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask);
+    
+    // Apply the final choice of mask
+    applyMask(&modulesGrid, &isFunctionGrid, mask);
+
+    return 0;
+}
+
+int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) {
+    return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data));
+}
+
+bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) {
+    if (x >= qrcode->size || y >= qrcode->size) {
+        return false;
+    }
+
+    uint32_t offset = y * qrcode->size + x;
+    return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
+}
+
+/*
+uint8_t qrcode_getHexLength(QRCode *qrcode) {
+    return ((qrcode->size * qrcode->size) + 7) / 4;
+}
+
+void qrcode_getHex(QRCode *qrcode, char *result) {
+    
+}
+*/

+ 100 - 0
qrcode.h

@@ -0,0 +1,100 @@
+/**
+ * The MIT License (MIT)
+ *
+ * This library is written and maintained by Richard Moore.
+ * Major parts were derived from Project Nayuki's library.
+ *
+ * Copyright (c) 2017 Richard Moore     (https://github.com/ricmoo/QRCode)
+ * Copyright (c) 2017 Project Nayuki    (https://www.nayuki.io/page/qr-code-generator-library)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ *  Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
+ *  heavily inspired and compared against.
+ *
+ *  See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
+ */
+
+
+#ifndef __QRCODE_H_
+#define __QRCODE_H_
+
+#ifndef __cplusplus
+// typedef unsigned char bool;
+// static const bool false = 0;
+// static const bool true = 1;
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+
+// QR Code Format Encoding
+#define MODE_NUMERIC        0
+#define MODE_ALPHANUMERIC   1
+#define MODE_BYTE           2
+
+
+// Error Correction Code Levels
+#define ECC_LOW            0
+#define ECC_MEDIUM         1
+#define ECC_QUARTILE       2
+#define ECC_HIGH           3
+
+
+// If set to non-zero, this library can ONLY produce QR codes at that version
+// This saves a lot of dynamic memory, as the codeword tables are skipped
+#ifndef LOCK_VERSION
+#define LOCK_VERSION       0
+#endif
+
+
+typedef struct QRCode {
+    uint8_t version;
+    uint8_t size;
+    uint8_t ecc;
+    uint8_t mode;
+    uint8_t mask;
+    uint8_t *modules;
+} QRCode;
+
+
+#ifdef __cplusplus
+extern "C"{
+#endif  /* __cplusplus */
+
+
+
+uint16_t qrcode_getBufferSize(uint8_t version);
+
+int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data);
+int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length);
+
+bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y);
+
+
+
+#ifdef __cplusplus
+}
+#endif  /* __cplusplus */
+
+
+#endif  /* __QRCODE_H_ */

+ 180 - 0
uart.c

@@ -0,0 +1,180 @@
+#include "uart.h"
+
+#define UART_CH (FuriHalUartIdUSART1)
+#define LP_UART_CH (FuriHalUartIdLPUART1)
+#define BAUDRATE (115200)
+
+struct Uart {
+    void* app;
+    FuriHalUartId channel;
+    FuriThread* rx_thread;
+    FuriStreamBuffer* rx_stream;
+    uint8_t rx_buf[RX_BUF_SIZE + 1];
+    void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context);
+};
+
+
+typedef enum {
+    WorkerEvtStop = (1 << 0),
+    WorkerEvtRxDone = (1 << 1),
+} WorkerEvtFlags;
+
+void uart_set_handle_rx_data_cb(
+    Uart* uart,
+    void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)) {
+    furi_assert(uart);
+    uart->handle_rx_data_cb = handle_rx_data_cb;
+}
+
+#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
+
+void uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
+    Uart* uart = (Uart*)context;
+
+    if(ev == UartIrqEventRXNE) {
+        furi_stream_buffer_send(uart->rx_stream, &data, 1, 0);
+        furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtRxDone);
+    }
+}
+
+
+// Define una constante para el prefijo que estamos buscando
+#define JSON_PREFIX "JSON:"
+
+// Variables globales
+static char json_buffer[2048]; // Ajusta el tamaño según tus necesidades
+static size_t json_buffer_index = 0;
+static bool json_capture_active = false;
+// static bool json_finded = false;
+
+// Prototipo de la función
+// static void process_json_buffer();
+
+
+static void process_json_buffer(void* context) {
+    Uart* uart = (Uart*)context;
+    // Agregamos el terminador nulo al final del buffer
+    json_buffer[json_buffer_index] = '\0';
+    if (uart->handle_rx_data_cb) {
+        uart->handle_rx_data_cb((uint8_t *)json_buffer, json_buffer_index, uart->app);
+        memset(json_buffer, 0, sizeof(json_buffer));
+    }
+    
+    // Reiniciamos el buffer
+    json_buffer_index = 0;
+}
+
+static void uart_echo_push_to_list(void* context, uint8_t data) {
+    Uart* uart = (Uart*)context;
+    if (!json_capture_active) {
+        if (data == JSON_PREFIX[json_buffer_index]) {
+            json_buffer[json_buffer_index++] = data; // Agregar el carácter al buffer
+            if (json_buffer_index == strlen(JSON_PREFIX)) {
+                // Encontramos el prefijo, comenzamos a capturar
+                json_buffer_index = 0;
+                json_capture_active = true;
+            }
+        } else {
+            // Reiniciamos el índice si no coincide con el prefijo
+            json_buffer_index = 0;
+        }
+    } else {
+        // Capturamos caracteres hasta encontrar '\n'
+        json_buffer[json_buffer_index++] = data;
+        if (data == '\n') {
+            // Terminamos de capturar la línea, procesamos el buffer
+            json_capture_active = false;
+            process_json_buffer(uart);
+        }
+    }
+}
+static int32_t uart_worker(void* context) {
+    furi_assert(context);
+    Uart* uart = (Uart*)context;
+
+    while(1) {
+        uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
+        furi_check((events & FuriFlagError) == 0);
+
+        if(events & WorkerEvtStop) break;
+        if(events & WorkerEvtRxDone) {
+            if(uart->channel == UART_CH) {
+                size_t length = 0;
+                do {
+                    uint8_t data[64];
+                    length = furi_stream_buffer_receive(uart->rx_stream, data, 64, 0);
+                    // FURI_LOG_I("UART", "[in]: %s", (char*)data);
+                    if(length > 0) {
+                        for(size_t i = 0; i < length; i++) {
+                            uart_echo_push_to_list(uart, data[i]);
+                        }
+                    }
+                } while(length > 0);
+            } else if(uart->channel == LP_UART_CH) {
+                size_t len = furi_stream_buffer_receive(uart->rx_stream, uart->rx_buf, RX_BUF_SIZE, 0);
+                if(len > 0) {
+                    if(uart->handle_rx_data_cb) uart->handle_rx_data_cb(uart->rx_buf, len, uart->app);
+                }
+            }
+        }
+    }
+    furi_stream_buffer_free(uart->rx_stream);
+
+    return 0;
+}
+
+void uart_tx(uint8_t* data, size_t len) {
+    furi_hal_uart_tx(UART_CH, data, len);
+}
+
+void lp_uart_tx(uint8_t* data, size_t len) {
+    furi_hal_uart_tx(LP_UART_CH, data, len);
+}
+
+Uart*
+    _uart_init(void* app, FuriHalUartId channel, const char* thread_name) {
+    Uart* uart = (Uart*)malloc(sizeof(Uart));
+
+    uart->app = app;
+    uart->channel = channel;
+    uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
+    uart->rx_thread = furi_thread_alloc();
+    furi_thread_set_name(uart->rx_thread, thread_name);
+    furi_thread_set_stack_size(uart->rx_thread, 1024);
+    furi_thread_set_context(uart->rx_thread, uart);
+    furi_thread_set_callback(uart->rx_thread, uart_worker);
+    furi_thread_start(uart->rx_thread);
+    if(channel == FuriHalUartIdUSART1) {
+        furi_hal_console_disable();
+    } else if(channel == FuriHalUartIdLPUART1) {
+        furi_hal_uart_init(channel, BAUDRATE);
+    }
+    furi_hal_uart_set_br(channel, BAUDRATE);
+    furi_hal_uart_set_irq_cb(channel, uart_on_irq_cb, uart);
+
+    return uart;
+}
+
+Uart* usart_init(void* app) {
+    return _uart_init(app, UART_CH, "UartRxThread");
+}
+
+Uart* lp_uart_init(void* app) {
+    return _uart_init(app, LP_UART_CH, "LPUartRxThread");
+}
+
+void uart_free(Uart* uart) {
+    furi_assert(uart);
+
+    furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtStop);
+    furi_thread_join(uart->rx_thread);
+    furi_thread_free(uart->rx_thread);
+
+    furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL);
+    if(uart->channel == FuriHalUartIdLPUART1) {
+        furi_hal_uart_deinit(uart->channel);
+    }
+    furi_hal_console_enable();
+
+    free(uart);
+}

+ 20 - 0
uart.h

@@ -0,0 +1,20 @@
+#ifndef UART_H
+#define UART_H
+
+#pragma once
+
+#include "furi_hal.h"
+
+
+#define RX_BUF_SIZE (1024)
+
+typedef struct Uart Uart;
+
+void uart_set_handle_rx_data_cb(Uart* uart, void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context));
+void uart_tx(uint8_t* data, size_t len);
+void lp_uart_tx(uint8_t* data, size_t len);
+Uart* usart_init(void* app);
+Uart* lp_uart_init(void* app);
+void uart_free(Uart* uart);
+
+#endif /* UART_H */