ekt: partial y3ws implementation

This commit is contained in:
2025-09-03 18:49:41 +02:00
parent 3327548ca6
commit dc631215b1
15 changed files with 6099 additions and 0 deletions

View File

@ -14,6 +14,10 @@ HRESULT y3_io_init() {
return S_OK;
}
HRESULT y3_io_close() {
return S_OK;
}
HRESULT y3_io_get_cards(struct CardInfo* pCardInfo, int* numCards) {
memset(pCardInfo, 0, sizeof(struct CardInfo) * *numCards);

View File

@ -0,0 +1,20 @@
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.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,306 @@
/*
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 18
#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
/* Limits the length of circular references can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_CIRCULAR_LIMIT
#define CJSON_CIRCULAR_LIMIT 10000
#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);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
cJSON_Invalid\
)
/* 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/*
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_Utils__h
#define cJSON_Utils__h
#ifdef __cplusplus
extern "C"
{
#endif
#include "cJSON.h"
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
/* Utility for generating patch array entries. */
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
/* Returns 0 for success. */
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
/*
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
//{
// cJSON *modme = cJSON_Duplicate(*object, 1);
// int error = cJSONUtils_ApplyPatches(modme, patches);
// if (!error)
// {
// cJSON_Delete(*object);
// *object = modme;
// }
// else
// {
// cJSON_Delete(modme);
// }
//
// return error;
//}
// Code not added to library since this strategy is a LOT slower.
*/
/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
/* target will be modified by patch. return value is new ptr for target. */
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
/* generates a patch to move from -> to */
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
/* Given a root object and a target object, construct a pointer from one to the other. */
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
/* Sorts the members of the object into alphabetical order. */
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,69 @@
/*
Copyright (c) 2014 Malte Hildingsson, malte (at) afterwi.se
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 AW_BASE64_H
#define AW_BASE64_H
#include <stddef.h>
#ifdef __AVR__
#include <avr/pgmspace.h>
#else
#define PROGMEM
#endif
#ifdef __cplusplus
extern "C" {
#endif
static inline size_t base64len(size_t n) {
return (n + 2) / 3 * 4;
}
static size_t base64(char *buf, size_t nbuf, const unsigned char *p, size_t n) {
const char t[64] PROGMEM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t i, m = base64len(n);
unsigned x;
if (nbuf >= m)
for (i = 0; i < n; ++i) {
x = p[i] << 0x10;
x |= (++i < n ? p[i] : 0) << 0x08;
x |= (++i < n ? p[i] : 0) << 0x00;
*buf++ = t[x >> 3 * 6 & 0x3f];
*buf++ = t[x >> 2 * 6 & 0x3f];
*buf++ = (((n - 0 - i) >> 31) & '=') |
(~((n - 0 - i) >> 31) & t[x >> 1 * 6 & 0x3f]);
*buf++ = (((n - 1 - i) >> 31) & '=') |
(~((n - 1 - i) >> 31) & t[x >> 0 * 6 & 0x3f]);
}
return m;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AW_BASE64_H */

View File

@ -0,0 +1,137 @@
/*
Copyright (c) 2014 Malte Hildingsson, malte (at) afterwi.se
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 AW_SHA1_H
#define AW_SHA1_H
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wparentheses"
#include <string.h>
#if _MSC_VER
# define _sha1_restrict __restrict
#else
# define _sha1_restrict __restrict__
#endif
#define SHA1_SIZE 20
#ifdef __cplusplus
extern "C" {
#endif
static inline void sha1mix(unsigned *_sha1_restrict r, unsigned *_sha1_restrict w) {
unsigned a = r[0];
unsigned b = r[1];
unsigned c = r[2];
unsigned d = r[3];
unsigned e = r[4];
unsigned t, i = 0;
#define rol(x,s) ((x) << (s) | (unsigned) (x) >> (32 - (s)))
#define mix(f,v) do { \
t = (f) + (v) + rol(a, 5) + e + w[i & 0xf]; \
e = d; \
d = c; \
c = rol(b, 30); \
b = a; \
a = t; \
} while (0)
for (; i < 16; ++i)
mix(d ^ (b & (c ^ d)), 0x5a827999);
for (; i < 20; ++i) {
w[i & 0xf] = rol(w[i + 13 & 0xf] ^ w[i + 8 & 0xf] ^ w[i + 2 & 0xf] ^ w[i & 0xf], 1);
mix(d ^ (b & (c ^ d)), 0x5a827999);
}
for (; i < 40; ++i) {
w[i & 0xf] = rol(w[i + 13 & 0xf] ^ w[i + 8 & 0xf] ^ w[i + 2 & 0xf] ^ w[i & 0xf], 1);
mix(b ^ c ^ d, 0x6ed9eba1);
}
for (; i < 60; ++i) {
w[i & 0xf] = rol(w[i + 13 & 0xf] ^ w[i + 8 & 0xf] ^ w[i + 2 & 0xf] ^ w[i & 0xf], 1);
mix((b & c) | (d & (b | c)), 0x8f1bbcdc);
}
for (; i < 80; ++i) {
w[i & 0xf] = rol(w[i + 13 & 0xf] ^ w[i + 8 & 0xf] ^ w[i + 2 & 0xf] ^ w[i & 0xf], 1);
mix(b ^ c ^ d, 0xca62c1d6);
}
#undef mix
#undef rol
r[0] += a;
r[1] += b;
r[2] += c;
r[3] += d;
r[4] += e;
}
static void sha1(unsigned char h[static SHA1_SIZE], const void *_sha1_restrict p, size_t n) {
size_t i = 0;
unsigned w[16], r[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
for (; i < (n & ~0x3f);) {
do w[i >> 2 & 0xf] =
((const unsigned char *) p)[i + 3] << 0x00 |
((const unsigned char *) p)[i + 2] << 0x08 |
((const unsigned char *) p)[i + 1] << 0x10 |
((const unsigned char *) p)[i + 0] << 0x18;
while ((i += 4) & 0x3f);
sha1mix(r, w);
}
memset(w, 0, sizeof w);
for (; i < n; ++i)
w[i >> 2 & 0xf] |= ((const unsigned char *) p)[i] << ((3 ^ i & 3) << 3);
w[i >> 2 & 0xf] |= 0x80 << ((3 ^ i & 3) << 3);
if ((n & 0x3f) > 56) {
sha1mix(r, w);
memset(w, 0, sizeof w);
}
w[15] = n << 3;
sha1mix(r, w);
for (i = 0; i < 5; ++i)
h[(i << 2) + 0] = (unsigned char) (r[i] >> 0x18),
h[(i << 2) + 1] = (unsigned char) (r[i] >> 0x10),
h[(i << 2) + 2] = (unsigned char) (r[i] >> 0x08),
h[(i << 2) + 3] = (unsigned char) (r[i] >> 0x00);
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#pragma GCC diagnostic pop
#endif /* AW_SHA1_H */

View File

@ -0,0 +1,328 @@
/*
* Copyright (c) 2014 Putilov Andrey
*
* 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.
*
*/
#include <stdio.h>
#include <windows.h>
#include <wtypes.h>
#include <winsock2.h>
#include "websocket.h"
static char rn[] PROGMEM = "\r\n";
void nullHandshake(struct handshake *hs)
{
hs->host = NULL;
hs->origin = NULL;
hs->resource = NULL;
hs->key = NULL;
hs->frameType = WS_EMPTY_FRAME;
}
void freeHandshake(struct handshake *hs)
{
if (hs->host) {
free(hs->host);
}
if (hs->origin) {
free(hs->origin);
}
if (hs->resource) {
free(hs->resource);
}
if (hs->key) {
free(hs->key);
}
nullHandshake(hs);
}
static char* getUptoLinefeed(const char *startFrom)
{
char *writeTo = NULL;
uint8_t newLength = strstr_P(startFrom, rn) - startFrom;
assert(newLength);
writeTo = (char *)malloc(newLength+1); //+1 for '\x00'
assert(writeTo);
memcpy(writeTo, startFrom, newLength);
writeTo[ newLength ] = 0;
return writeTo;
}
enum wsFrameType wsParseHandshake(const uint8_t *inputFrame, size_t inputLength,
struct handshake *hs)
{
const char *inputPtr = (const char *)inputFrame;
const char *endPtr = (const char *)inputFrame + inputLength;
if (!strstr((const char *)inputFrame, "\r\n\r\n"))
return WS_INCOMPLETE_FRAME;
if (memcmp_P(inputFrame, PSTR("GET "), 4) != 0)
return WS_ERROR_FRAME;
// measure resource size
char *first = strchr((const char *)inputFrame, ' ');
if (!first)
return WS_ERROR_FRAME;
first++;
char *second = strchr(first, ' ');
if (!second)
return WS_ERROR_FRAME;
if (hs->resource) {
free(hs->resource);
hs->resource = NULL;
}
hs->resource = (char *)malloc(second - first + 1); // +1 is for \x00 symbol
assert(hs->resource);
if (sscanf_P(inputPtr, PSTR("GET %s HTTP/1.1\r\n"), hs->resource) != 1)
return WS_ERROR_FRAME;
inputPtr = strstr_P(inputPtr, rn) + 2;
/*
parse next lines
*/
#define prepare(x) do {if (x) { free(x); x = NULL; }} while(0)
#define strtolower(x) do { int i; for (i = 0; x[i]; i++) x[i] = tolower(x[i]); } while(0)
uint8_t connectionFlag = FALSE;
uint8_t upgradeFlag = FALSE;
uint8_t subprotocolFlag = FALSE;
uint8_t versionMismatch = FALSE;
while (inputPtr < endPtr && inputPtr[0] != '\r' && inputPtr[1] != '\n') {
if (memcmp_P(inputPtr, hostField, strlen_P(hostField)) == 0) {
inputPtr += strlen_P(hostField);
prepare(hs->host);
hs->host = getUptoLinefeed(inputPtr);
} else
if (memcmp_P(inputPtr, originField, strlen_P(originField)) == 0) {
inputPtr += strlen_P(originField);
prepare(hs->origin);
hs->origin = getUptoLinefeed(inputPtr);
} else
if (memcmp_P(inputPtr, protocolField, strlen_P(protocolField)) == 0) {
inputPtr += strlen_P(protocolField);
subprotocolFlag = TRUE;
} else
if (memcmp_P(inputPtr, keyField, strlen_P(keyField)) == 0) {
inputPtr += strlen_P(keyField);
prepare(hs->key);
hs->key = getUptoLinefeed(inputPtr);
} else
if (memcmp_P(inputPtr, versionField, strlen_P(versionField)) == 0) {
inputPtr += strlen_P(versionField);
char *versionString = NULL;
versionString = getUptoLinefeed(inputPtr);
if (memcmp_P(versionString, version, strlen_P(version)) != 0)
versionMismatch = TRUE;
free(versionString);
} else
if (memcmp_P(inputPtr, connectionField, strlen_P(connectionField)) == 0) {
inputPtr += strlen_P(connectionField);
char *connectionValue = NULL;
connectionValue = getUptoLinefeed(inputPtr);
strtolower(connectionValue);
assert(connectionValue);
if (strstr_P(connectionValue, upgrade) != NULL)
connectionFlag = TRUE;
free(connectionValue);
} else
if (memcmp_P(inputPtr, upgradeField, strlen_P(upgradeField)) == 0) {
inputPtr += strlen_P(upgradeField);
char *compare = NULL;
compare = getUptoLinefeed(inputPtr);
strtolower(compare);
assert(compare);
if (memcmp_P(compare, websocket, strlen_P(websocket)) == 0)
upgradeFlag = TRUE;
free(compare);
};
inputPtr = strstr_P(inputPtr, rn) + 2;
}
// we have read all data, so check them
if (!hs->host || !hs->key || !connectionFlag || !upgradeFlag || subprotocolFlag
|| versionMismatch)
{
hs->frameType = WS_ERROR_FRAME;
} else {
hs->frameType = WS_OPENING_FRAME;
}
return hs->frameType;
}
void wsGetHandshakeAnswer(const struct handshake *hs, uint8_t *outFrame,
size_t *outLength)
{
assert(outFrame && *outLength);
assert(hs->frameType == WS_OPENING_FRAME);
assert(hs && hs->key);
char *responseKey = NULL;
uint8_t length = strlen(hs->key)+strlen_P(secret);
responseKey = malloc(length);
memcpy(responseKey, hs->key, strlen(hs->key));
memcpy_P(&(responseKey[strlen(hs->key)]), secret, strlen_P(secret));
unsigned char shaHash[20];
memset(shaHash, 0, sizeof(shaHash));
sha1(shaHash, responseKey, length);
size_t base64Length = base64(responseKey, length, shaHash, 20);
responseKey[base64Length] = '\0';
int written = sprintf_P((char *)outFrame,
PSTR("HTTP/1.1 101 Switching Protocols\r\n"
"%s%s\r\n"
"%s%s\r\n"
"Sec-WebSocket-Accept: %s\r\n\r\n"),
upgradeField,
websocket,
connectionField,
upgrade2,
responseKey);
free(responseKey);
// if assert fail, that means, that we corrupt memory
assert(written <= *outLength);
*outLength = written;
}
void wsMakeFrame(const uint8_t *data, size_t dataLength,
uint8_t *outFrame, size_t *outLength, enum wsFrameType frameType)
{
assert(outFrame && *outLength);
assert(frameType < 0x10);
if (dataLength > 0)
assert(data);
outFrame[0] = 0x80 | frameType;
if (dataLength <= 125) {
outFrame[1] = dataLength;
*outLength = 2;
} else if (dataLength <= 0xFFFF) {
outFrame[1] = 126;
uint16_t payloadLength16b = htons(dataLength);
memcpy(&outFrame[2], &payloadLength16b, 2);
*outLength = 4;
} else {
assert(dataLength <= 0xFFFF);
/* implementation for 64bit systems
outFrame[1] = 127;
dataLength = htonll(dataLength);
memcpy(&outFrame[2], &dataLength, 8);
*outLength = 10;
*/
}
memcpy(&outFrame[*outLength], data, dataLength);
*outLength+= dataLength;
}
static size_t getPayloadLength(const uint8_t *inputFrame, size_t inputLength,
uint8_t *payloadFieldExtraBytes, enum wsFrameType *frameType)
{
size_t payloadLength = inputFrame[1] & 0x7F;
*payloadFieldExtraBytes = 0;
if ((payloadLength == 0x7E && inputLength < 4) || (payloadLength == 0x7F && inputLength < 10)) {
*frameType = WS_INCOMPLETE_FRAME;
return 0;
}
if (payloadLength == 0x7F && (inputFrame[3] & 0x80) != 0x0) {
*frameType = WS_ERROR_FRAME;
return 0;
}
if (payloadLength == 0x7E) {
uint16_t payloadLength16b = 0;
*payloadFieldExtraBytes = 2;
memcpy(&payloadLength16b, &inputFrame[2], *payloadFieldExtraBytes);
payloadLength = ntohs(payloadLength16b);
} else if (payloadLength == 0x7F) {
*frameType = WS_ERROR_FRAME;
return 0;
/* // implementation for 64bit systems
uint64_t payloadLength64b = 0;
*payloadFieldExtraBytes = 8;
memcpy(&payloadLength64b, &inputFrame[2], *payloadFieldExtraBytes);
if (payloadLength64b > SIZE_MAX) {
*frameType = WS_ERROR_FRAME;
return 0;
}
payloadLength = (size_t)ntohll(payloadLength64b);
*/
}
return payloadLength;
}
enum wsFrameType wsParseInputFrame(uint8_t *inputFrame, size_t inputLength,
uint8_t **dataPtr, size_t *dataLength)
{
assert(inputFrame && inputLength);
if (inputLength < 2)
return WS_INCOMPLETE_FRAME;
if ((inputFrame[0] & 0x70) != 0x0) // checks extensions off
return WS_ERROR_FRAME;
if ((inputFrame[0] & 0x80) != 0x80) // we haven't continuation frames support
return WS_ERROR_FRAME; // so, fin flag must be set
if ((inputFrame[1] & 0x80) != 0x80) // checks masking bit
return WS_ERROR_FRAME;
uint8_t opcode = inputFrame[0] & 0x0F;
if (opcode == WS_TEXT_FRAME ||
opcode == WS_BINARY_FRAME ||
opcode == WS_CLOSING_FRAME ||
opcode == WS_PING_FRAME ||
opcode == WS_PONG_FRAME
){
enum wsFrameType frameType = opcode;
uint8_t payloadFieldExtraBytes = 0;
size_t payloadLength = getPayloadLength(inputFrame, inputLength,
&payloadFieldExtraBytes, &frameType);
if (payloadLength > 0) {
if (payloadLength + 6 + payloadFieldExtraBytes > inputLength) // 4-maskingKey, 2-header
return WS_INCOMPLETE_FRAME;
uint8_t *maskingKey = &inputFrame[2 + payloadFieldExtraBytes];
assert(payloadLength == inputLength - 6 - payloadFieldExtraBytes);
*dataPtr = &inputFrame[2 + payloadFieldExtraBytes + 4];
*dataLength = payloadLength;
size_t i;
for (i = 0; i < *dataLength; i++) {
(*dataPtr)[i] = (*dataPtr)[i] ^ maskingKey[i%4];
}
}
return frameType;
}
return WS_ERROR_FRAME;
}

View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2014 Putilov Andrey
*
* 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 WEBSOCKET_H
#define WEBSOCKET_H
#ifdef __cplusplus
extern "C" {
#endif
#include <assert.h>
#include <stdint.h> /* uint8_t */
#include <stdlib.h> /* strtoul */
#include <string.h>
#include <stdio.h> /* sscanf */
#include <ctype.h> /* isdigit */
//#include <stddef.h> /* size_t */
#include "aw-base64.h"
#include "aw-sha1.h"
#ifdef __AVR__
#include <avr/pgmspace.h>
#else
#define PROGMEM
#define PSTR
#define strstr_P strstr
#define sscanf_P sscanf
#define sprintf_P sprintf
#define strlen_P strlen
#define memcmp_P memcmp
#define memcpy_P memcpy
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
static const char connectionField[] PROGMEM = "Connection: ";
static const char upgrade[] PROGMEM = "upgrade";
static const char upgrade2[] PROGMEM = "Upgrade";
static const char upgradeField[] PROGMEM = "Upgrade: ";
static const char websocket[] PROGMEM = "websocket";
static const char hostField[] PROGMEM = "Host: ";
static const char originField[] PROGMEM = "Origin: ";
static const char keyField[] PROGMEM = "Sec-WebSocket-Key: ";
static const char protocolField[] PROGMEM = "Sec-WebSocket-Protocol: ";
static const char versionField[] PROGMEM = "Sec-WebSocket-Version: ";
static const char version[] PROGMEM = "13";
static const char secret[] PROGMEM = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
enum wsFrameType { // errors starting from 0xF0
WS_EMPTY_FRAME = 0xF0,
WS_ERROR_FRAME = 0xF1,
WS_INCOMPLETE_FRAME = 0xF2,
WS_TEXT_FRAME = 0x01,
WS_BINARY_FRAME = 0x02,
WS_PING_FRAME = 0x09,
WS_PONG_FRAME = 0x0A,
WS_OPENING_FRAME = 0xF3,
WS_CLOSING_FRAME = 0x08
};
enum wsState {
WS_STATE_OPENING,
WS_STATE_NORMAL,
WS_STATE_CLOSING
};
struct handshake {
char *host;
char *origin;
char *key;
char *resource;
enum wsFrameType frameType;
};
/**
* @param inputFrame Pointer to input frame
* @param inputLength Length of input frame
* @param hs Cleared with nullHandshake() handshake structure
* @return Type of parsed frame
*/
enum wsFrameType wsParseHandshake(const uint8_t *inputFrame, size_t inputLength,
struct handshake *hs);
/**
* @param hs Filled handshake structure
* @param outFrame Pointer to frame buffer
* @param outLength Length of frame buffer. Return length of out frame
*/
void wsGetHandshakeAnswer(const struct handshake *hs, uint8_t *outFrame,
size_t *outLength);
/**
* @param data Pointer to input data array
* @param dataLength Length of data array
* @param outFrame Pointer to frame buffer
* @param outLength Length of out frame buffer. Return length of out frame
* @param frameType [WS_TEXT_FRAME] frame type to build
*/
void wsMakeFrame(const uint8_t *data, size_t dataLength,
uint8_t *outFrame, size_t *outLength, enum wsFrameType frameType);
/**
*
* @param inputFrame Pointer to input frame. Frame will be modified.
* @param inputLen Length of input frame
* @param outDataPtr Return pointer to extracted data in input frame
* @param outLen Return length of extracted data
* @return Type of parsed frame
*/
enum wsFrameType wsParseInputFrame(uint8_t *inputFrame, size_t inputLength,
uint8_t **dataPtr, size_t *dataLength);
/**
* @param hs NULL handshake structure
*/
void nullHandshake(struct handshake *hs);
/**
* @param hs free and NULL handshake structure
*/
void freeHandshake(struct handshake *hs);
#ifdef __cplusplus
}
#endif
#endif /* WEBSOCKET_H */

View File

@ -0,0 +1,310 @@
#include <stdint.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include "3rdparty/websockets/websocket.h"
#include "3rdparty/cjson/cJSON.h"
#include "util/dprintf.h"
#include "y3io/y3.h"
DWORD __stdcall y3_websocket_thread_proc(LPVOID ctx);
// TODO: make these settings configurable
#define Y3WS_DEBUG true
#define PORT 3497
#define WS_PATH "/y3io"
#define PROTOCOL_VERSION 1
static bool is_initialized = false;
static SOCKET ws_socket = 0;
static HANDLE server_thread = INVALID_HANDLE_VALUE;
static struct CardInfo card_info[32];
static int card_info_size = 0;
static CRITICAL_SECTION card_info_lock;
uint16_t y3_io_get_api_version() {
return 0x0100;
}
HRESULT y3_io_init() {
if (is_initialized) {
return S_FALSE;
}
memset(card_info, 0, sizeof(card_info));
InitializeCriticalSection(&card_info_lock);
ws_socket = socket(AF_INET, SOCK_STREAM, 0);
if (ws_socket == -1) {
dprintf("Y3WS: Failed to create socket\n");
return E_FAIL;
}
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(PORT);
if (bind(ws_socket, (struct sockaddr *) &local, sizeof(local)) == -1) {
dprintf("Y3WS: Failed to bind socket\n");
return E_FAIL;
}
if (listen(ws_socket, 1) == -1) {
dprintf("Y3WS: Failed to listen on socket\n");
return E_FAIL;
}
server_thread = CreateThread(NULL, 0, y3_websocket_thread_proc, NULL, 0, NULL);
is_initialized = true;
dprintf("Y3WS: Started server on port %d\n", PORT);
return S_OK;
}
HRESULT y3_io_close() {
dprintf("Y3WS: Exiting\n");
if (ws_socket != 0) {
closesocket(ws_socket);
ws_socket = 0;
}
if (server_thread != NULL) {
WaitForSingleObject(server_thread, INFINITE);
server_thread = INVALID_HANDLE_VALUE;
}
is_initialized = false;
return S_OK;
}
int safeSend(SOCKET client, const uint8_t* buffer, size_t bufferSize) {
ssize_t written = send(client, (char *) buffer, (int) bufferSize, 0);
if (written == -1) {
closesocket(client);
dprintf("Y3WS: send failed");
return EXIT_FAILURE;
}
if (written != bufferSize) {
closesocket(client);
perror("Y3WS: connection interrupted");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static void y3ws_make_error_packet(const char* message, uint8_t* output_data, size_t* output_size) {
dprintf("Y3WS: Error: %s\n", message);
*output_size = sprintf_s((char*)output_data, *output_size, "{\"version\":\"%d\", \"success\":false,\"error\":\"%s\"}", PROTOCOL_VERSION, message);
}
static void y3ws_make_success_packet(uint8_t* output_data, size_t* output_size) {
*output_size = sprintf_s((char*)output_data, *output_size, "{\"version\":\"%d\", \"success\":true}", PROTOCOL_VERSION);
}
static void y3ws_handle(const char* input_data, uint32_t input_length, uint8_t* output_data, size_t* output_size) {
#if Y3WS_DEBUG
dprintf("Y3WS: Message was: %s\n", input_data);
#endif
cJSON* json = cJSON_ParseWithLength(input_data, input_length);
if (json == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
dprintf("Y3WS: Invalid JSON received!\n");
dprintf("Y3WS: Message was: %s\n", input_data);
dprintf("Y3WS: Error at: %s\n", error_ptr);
}
const cJSON* ver = cJSON_GetObjectItemCaseSensitive(json, "version");
const cJSON* cmd = cJSON_GetObjectItemCaseSensitive(json, "command");
if (!cJSON_IsNumber(ver) || ver->valueint <= 0) {
y3ws_make_error_packet("Missing version attribute", output_data, output_size);
goto end;
}
if (ver->valueint != PROTOCOL_VERSION) {
y3ws_make_error_packet("Incompatible version", output_data, output_size);
goto end;
}
if (strcmp(cmd->valuestring, "get_game_id") == 0) {
// TODO: send current game id (SDGY, SDDD)
} else if (strcmp(cmd->valuestring, "get_cards") == 0) {
// TODO: get array of {image_filename, card_id}
} else if (strcmp(cmd->valuestring, "set_field") == 0) {
const cJSON* cards = cJSON_GetObjectItemCaseSensitive(json, "cards");
const cJSON* card = NULL;
card_info_size = 0;
EnterCriticalSection(&card_info_lock);
memset(card_info, 0, sizeof(card_info));
cJSON_ArrayForEach(card, cards) {
cJSON* x = cJSON_GetObjectItemCaseSensitive(card, "x");
cJSON* y = cJSON_GetObjectItemCaseSensitive(card, "y");
cJSON* r = cJSON_GetObjectItemCaseSensitive(card, "rotation");
cJSON* id = cJSON_GetObjectItemCaseSensitive(card, "id");
if (cJSON_IsNumber(x) && cJSON_IsNumber(y) && cJSON_IsNumber(r) && cJSON_IsNumber(id)) {
card_info[card_info_size].eCardStatus = VALID;
card_info[card_info_size].fX = (float)x->valuedouble;
card_info[card_info_size].fY = (float)y->valuedouble;
card_info[card_info_size].fAngle = (float)r->valuedouble;
card_info[card_info_size].uID = r->valueint;
}
card_info_size++;
}
LeaveCriticalSection(&card_info_lock);
y3ws_make_success_packet(output_data, output_size);
} else {
y3ws_make_error_packet("Unknown command", output_data, output_size);
}
end:
cJSON_Delete(json);
}
static void y3ws_handle_messages(SOCKET client) {
enum wsFrameType frameType = WS_INCOMPLETE_FRAME;
enum wsState state = WS_STATE_OPENING;
const int BUFFER_SIZE = 4096;
uint8_t in_buf[BUFFER_SIZE];
uint8_t out_buf[BUFFER_SIZE];
size_t out_size;
uint8_t* message = NULL;
size_t message_size = 0;
struct handshake hs;
nullHandshake(&hs);
while (frameType == WS_INCOMPLETE_FRAME) {
ssize_t read = recv(client, (char *) in_buf, BUFFER_SIZE, 0);
if (!read) {
dprintf("Y3WS: Accept failed\n");
break;
}
if (state == WS_STATE_OPENING) {
frameType = wsParseHandshake(in_buf, read, &hs);
} else {
frameType = wsParseInputFrame(in_buf, read, &message, &message_size);
}
if ((frameType == WS_INCOMPLETE_FRAME && read == BUFFER_SIZE) || frameType == WS_ERROR_FRAME) {
if (frameType == WS_INCOMPLETE_FRAME) {
dprintf("Y3WS: buffer too small");
} else {
dprintf("Y3WS: error in incoming frame\n");
}
if (state == WS_STATE_OPENING) {
memset(out_buf, 0, BUFFER_SIZE);
out_size = sprintf((char *) out_buf,
"HTTP/1.1 400 Bad Request\r\n"
"%s%s\r\n\r\n",
versionField,
version);
safeSend(client, out_buf, out_size);
break;
} else {
memset(out_buf, 0, BUFFER_SIZE);
wsMakeFrame(NULL, 0, out_buf, &out_size, WS_CLOSING_FRAME);
if (safeSend(client, out_buf, out_size) == EXIT_FAILURE) {
break;
}
state = WS_STATE_CLOSING;
frameType = WS_INCOMPLETE_FRAME;
}
}
if (state == WS_STATE_OPENING) {
assert(frameType == WS_OPENING_FRAME);
if (frameType == WS_OPENING_FRAME) {
if (strcmp(hs.resource, WS_PATH) != 0) {
dprintf("Y3WS: Invalid path: %s\n", hs.resource);
memset(out_buf, 0, BUFFER_SIZE);
out_size = sprintf((char *) out_buf, "HTTP/1.1 404 Not Found\r\n\r\n");
safeSend(client, out_buf, out_size);
break;
}
memset(out_buf, 0, BUFFER_SIZE);
wsGetHandshakeAnswer(&hs, out_buf, &out_size);
freeHandshake(&hs);
if (safeSend(client, out_buf, out_size) == EXIT_FAILURE)
break;
state = WS_STATE_NORMAL;
frameType = WS_INCOMPLETE_FRAME;
}
} else {
if (frameType == WS_CLOSING_FRAME) {
if (state == WS_STATE_CLOSING) {
break;
} else {
memset(out_buf, 0, BUFFER_SIZE);
wsMakeFrame(NULL, 0, out_buf, &out_size, WS_CLOSING_FRAME);
safeSend(client, out_buf, out_size);
break;
}
} else if (frameType == WS_TEXT_FRAME) {
uint8_t* input_data = malloc(message_size + 1);
memcpy(input_data, message, message_size);
input_data[message_size] = 0;
uint8_t* output_data = malloc(BUFFER_SIZE);
size_t output_size = BUFFER_SIZE;
y3ws_handle((const char*)input_data, message_size, output_data, &output_size);
wsMakeFrame(output_data, output_size, out_buf, &out_size, WS_TEXT_FRAME);
free(input_data);
if (safeSend(client, out_buf, out_size) == EXIT_FAILURE) {
break;
}
frameType = WS_INCOMPLETE_FRAME;
}
}
}
closesocket(client);
}
DWORD __stdcall y3_websocket_thread_proc(LPVOID ctx) {
while (true) {
struct sockaddr_in remote;
socklen_t sockaddr_len = sizeof(remote);
SOCKET client = accept(ws_socket, (struct sockaddr *) &remote, &sockaddr_len);
if (client == -1) {
break;
}
dprintf("Y3WS: Connected %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
y3ws_handle_messages(client);
dprintf("Y3WS: Disconnected\n");
}
closesocket(ws_socket);
return 0;
}
HRESULT y3_io_get_cards(struct CardInfo* pCardInfo, int* numCards) {
EnterCriticalSection(&card_info_lock);
for (int i = 0; i < card_info_size; i++) {
memcpy(&pCardInfo[i], &card_info[i], sizeof(struct CardInfo));
}
*numCards = card_info_size;
LeaveCriticalSection(&card_info_lock);
return S_OK;
}

View File

@ -18,6 +18,15 @@ y3io_lib = static_library(
'y3.c',
'y3.h',
'impl/dummy/y3io.c',
'impl/websockets/y3io.c',
'impl/websockets/3rdparty/websockets/aw-base64.h',
'impl/websockets/3rdparty/websockets/aw-sha1.h',
'impl/websockets/3rdparty/websockets/websocket.c',
'impl/websockets/3rdparty/websockets/websocket.h',
'impl/websockets/3rdparty/cjson/cJSON.c',
'impl/websockets/3rdparty/cjson/cJSON.h',
'impl/websockets/3rdparty/cjson/cJSON_Utils.c',
'impl/websockets/3rdparty/cjson/cJSON_Utils.h',
'impl/y3io.h',
],
)

View File

@ -16,6 +16,9 @@ const struct dll_bind_sym y3_dll_syms[] = {
}, {
.sym = "y3_io_get_cards",
.off = offsetof(struct y3_dll, get_cards),
}, {
.sym = "y3_io_close",
.off = offsetof(struct y3_dll, close),
}
};

View File

@ -10,6 +10,7 @@
struct y3_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*close)(void);
HRESULT (*get_cards)(struct CardInfo* cards, int* len);
};

View File

@ -17,6 +17,7 @@ EXPORTS
ekt_io_led_set_colors
y3_io_get_api_version
y3_io_init
y3_io_close
y3_io_get_cards
API_DLLVersion @1
API_GetLastError @2