├── res ├── font.otf ├── logo.png ├── icon-A-54.png ├── icon-B-54.png ├── icon-Y-54.png ├── background.png ├── icon-dpad-54.png ├── settings.json ├── skin.json ├── English.json └── coremapping.json ├── screenshots ├── main.png └── start.png ├── makefile ├── src ├── SDL_rotozoom.h ├── config.h ├── cJSON.h ├── SDL_rotozoom.c ├── main.c └── cJSON.c ├── readme.md └── LICENSE /res/font.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/res/font.otf -------------------------------------------------------------------------------- /res/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/res/logo.png -------------------------------------------------------------------------------- /res/icon-A-54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/res/icon-A-54.png -------------------------------------------------------------------------------- /res/icon-B-54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/res/icon-B-54.png -------------------------------------------------------------------------------- /res/icon-Y-54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/res/icon-Y-54.png -------------------------------------------------------------------------------- /res/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/res/background.png -------------------------------------------------------------------------------- /res/icon-dpad-54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/res/icon-dpad-54.png -------------------------------------------------------------------------------- /screenshots/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/screenshots/main.png -------------------------------------------------------------------------------- /screenshots/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgelessArchangel/RomSearch-for-RG35xx-Garlic-OS/HEAD/screenshots/start.png -------------------------------------------------------------------------------- /res/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "text-alignment": "left", 3 | "text-margin": 352, 4 | "color-guide": "#ffffff", 5 | "color-inactive": "#6b6b6b", 6 | "color-active": "#ffffff" 7 | } 8 | -------------------------------------------------------------------------------- /res/skin.json: -------------------------------------------------------------------------------- 1 | { 2 | "color-background": "#343434", 3 | "color-searchbox-frame": "#464448", 4 | "color-searchbox": "#232323", 5 | "color-search-text": "#dcdcdc", 6 | "color-key": "#464448", 7 | "color-selected-key-text": "#7d7d7d", 8 | "color-unselected-key-text": "#dcdcdc", 9 | "color-text-info": "#7d7d7d", 10 | "color-cursor": "#dcdcdc", 11 | "keyboard-and-result-pos-y": 105, 12 | "system-pos-x": 435, 13 | "system-pos-y": 25, 14 | "hide-helper-buttons": 0 15 | } 16 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET = romSearch 3 | 4 | CC = $(CROSS_COMPILE)gcc 5 | CFLAGS = -Os -D_GNU_SOURCE -marm -mtune=cortex-a9 -mfpu=neon-fp16 -mfloat-abi=softfp -march=armv7-a 6 | LDFLAGS = -ldl -lSDL -lSDL_image -lSDL_ttf -lpthread -lm 7 | all: 8 | @mkdir -p build/res 9 | $(CC) ./src/main.c ./src/cJSON.c ./src/SDL_rotozoom.c -o ./build/$(TARGET) $(CFLAGS) $(LDFLAGS) 10 | @cp ./res/* ./build/res 11 | 12 | linux: 13 | $(MAKE) CC=gcc CROSS_COMPILE="" CFLAGS="-D_GNU_SOURCE" --no-print-directory 14 | 15 | .PHONY: clean 16 | 17 | clean: 18 | rm -fr ./build 19 | -------------------------------------------------------------------------------- /res/English.json: -------------------------------------------------------------------------------- 1 | { 2 | "iso-code": "en_us", 3 | "font": "font.otf", 4 | "font-size": 20, 5 | "button-guide-font-size": 22, 6 | "recent-label": "Recent", 7 | "favorites-label": "Favorites", 8 | "consoles-label": "Games", 9 | "retroarch-label": "RetroArch", 10 | "settings-label": "Settings", 11 | "language-label": "Language", 12 | "navigate-label": "MOVE", 13 | "open-label": "OPEN", 14 | "back-label": "BACK", 15 | "favorite-label": "FAVORITE", 16 | "remove-label": "REMOVE", 17 | "empty-label": "Nothing to see here! Move along!", 18 | "savestates-unsupported": "This core doesn't support savestates!", 19 | "on-label": "ON", 20 | "off-label": "OFF", 21 | "am-label": "AM", 22 | "pm-label": "PM", 23 | "year-label": "YY", 24 | "month-label": "MM", 25 | "day-label": "DD", 26 | "hour-label": "HH", 27 | "minute-label": "MM", 28 | "meridian-time-label": "MT", 29 | "title-label": "Main Menu" 30 | } 31 | -------------------------------------------------------------------------------- /src/SDL_rotozoom.h: -------------------------------------------------------------------------------- 1 | #ifndef _SDL_rotozoom_h 2 | #define _SDL_rotozoom_h 3 | 4 | /* Set up for C function definitions, even when using C++ */ 5 | #ifdef __cplusplus 6 | extern "C" 7 | { 8 | #endif 9 | 10 | #include 11 | #ifndef M_PI 12 | #define M_PI 3.141592654 13 | #endif 14 | #include 15 | 16 | /* ---- Defines */ 17 | 18 | #define SMOOTHING_OFF 0 19 | #define SMOOTHING_ON 1 20 | 21 | /* ---- Structures */ 22 | 23 | typedef struct tColorRGBA 24 | { 25 | Uint8 r; 26 | Uint8 g; 27 | Uint8 b; 28 | Uint8 a; 29 | } 30 | tColorRGBA; 31 | 32 | typedef struct tColorY 33 | { 34 | Uint8 y; 35 | } 36 | tColorY; 37 | 38 | 39 | /* ---- Prototypes */ 40 | 41 | /* 42 | 43 | rotozoomSurface() 44 | 45 | Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. 46 | 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1 47 | then the destination 32bit surface is anti-aliased. If the surface is not 8bit 48 | or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. 49 | 50 | */ 51 | 52 | SDL_Surface *rotozoomSurface (SDL_Surface * src, double angle, double zoom, 53 | int smooth); 54 | 55 | 56 | /* 57 | 58 | zoomSurface() 59 | 60 | Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. 61 | 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1 62 | then the destination 32bit surface is anti-aliased. If the surface is not 8bit 63 | or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. 64 | 65 | */ 66 | 67 | SDL_Surface *zoomSurface (SDL_Surface * src, double zoomx, double zoomy, 68 | int smooth); 69 | 70 | 71 | /* Ends C function definitions when using C++ */ 72 | #ifdef __cplusplus 73 | }; 74 | #endif 75 | 76 | #endif /* _SDL_rotozoom_h */ 77 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ROM Search 2 | 3 | ROM Search for Anbernic RG35XX (Garlic OS). 4 | 5 | 6 | 7 | ## Details 8 | 9 | - ### This software is provided as is! Please back up your card! I am not responsible for any data you may lose! 10 | - This was created for personal use. 11 | - Compiled for Garlic OS using nfriedly / miyoo-toolchain 12 | - The app shows the first 100 roms found that match the search pattern. 13 | - For mame roms `"ARCADE", "CPS1", "CPS2", "CPS3", "FBNEO", "MAME2000", "NEOGEOCD", "NEOGEO"` the search is done based on mame.csv 14 | - The app tries to follow the Garlic OS theme. 15 | - The colors of the keyboard can be changed by adjusting `skin.json` file 16 | - The app tries to find the need files in the CFW. If a file is missing or broken, the app will use the files in the resource folder. 17 | - The app shoudl work in both single-sd and double-sd configuration! It will try to search for ROMs on both SD cards. 18 | 19 | ## Key Shortcuts 20 | - When keyboard is showing 21 | ``` 22 | A - Type key 23 | B - Backspace 24 | X - Swap left/right 25 | Y - Hide Keyboard 26 | Select - Space 27 | Start - Enter/Hide keyboard 28 | Menu - Back to main menu 29 | ``` 30 | - When the keyboard is hidden 31 | ``` 32 | A - Open ROM 33 | B - Back to main menu 34 | Y - Show keyboard 35 | Menu - Back to main menu 36 | ``` 37 | 38 | ## Installation 39 | 40 | - ##### Back up your `../CFW/start` file. The following changes will be performed 41 | 42 | - copy the folders to the `Roms` partition 43 | - in a dual card setup `CFW` folder must be copied to SD1 card and the `ROMS` folder can be copied to either SD1 or SD2. 44 | 45 | ## Technical details 46 | 47 | - Before launching an app or a ROM via retroarch, Garic OS creates a file `command.sh` in the `../CFW/retroarch` folder. 48 | - After the script is created, the main app closes and the script will be run 49 | - This is done to free up the RAM allocated by the main app 50 | - romSearch does a similar thing, creating the `romsearch.sh` at the same location 51 | - I could not find a way to implement the same behaviour without changing the start script. 52 | -------------------------------------------------------------------------------- /res/coremapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "FBA2012": "fbalpha2012_libretro.so", 3 | "FBNEO": "fbneo_libretro.so", 4 | "MAME2000": "mame2000_libretro.so", 5 | "ARCADE": "km_mame2003_xtreme_libretro.so", 6 | "CPS1": "fbalpha2012_libretro.so", 7 | "CPS2": "fbalpha2012_libretro.so", 8 | "CPS3": "fbalpha2012_libretro.so", 9 | "CPC": "crocods_libretro.so", 10 | "EIGHTHUNDRED": "atari800_libretro.so", 11 | "ATARI": "stella2014_libretro.so", 12 | "FIFTYTWOHUNDRED": "a5200_libretro.so", 13 | "SEVENTYEIGHTHUNDRED": "prosystem_libretro.so", 14 | "JAGUAR": "virtualjaguar_libretro.so", 15 | "ATARIST": "hatari_libretro.so", 16 | "COLECO": "bluemsx_libretro.so", 17 | "COMMODORE": "vice_x64_libretro.so", 18 | "VIC20": "vice_xvic_libretro.so", 19 | "AMIGA": "puae2021_libretro.so", 20 | "AMIGACD": "puae2021_libretro.so", 21 | "FAIRCHILD": "freechaf_libretro.so", 22 | "VECTREX": "vecx_libretro.so", 23 | "ODYSSEY": "o2em_libretro.so", 24 | "INTELLIVISION": "freeintv_libretro.so", 25 | "DOS": "dosbox_pure_libretro.so", 26 | "SGFX": "mednafen_supergrafx_libretro.so", 27 | "PCEIGHTYEIGHT": "quasi88_libretro.so", 28 | "PCNINETYEIGHT": "nekop2_libretro.so", 29 | "PCFX": "mednafen_pcfx_libretro.so", 30 | "PCE": "mednafen_pce_fast_libretro.so", 31 | "FC": "fceumm_libretro.so", 32 | "SFC": "mednafen_supafaust_libretro.so", 33 | "VB": "mednafen_vb_libretro.so", 34 | "PANASONIC": "opera_libretro.so", 35 | "VIDEOPAC": "o2em_libretro.so", 36 | "MD": "picodrive_libretro.so", 37 | "MS": "picodrive_libretro.so", 38 | "SEGASGONE": "gearsystem_libretro.so", 39 | "XONE": "x1_libretro.so", 40 | "X68000": "px68k_libretro.so", 41 | "ZXS": "fuse_libretro.so", 42 | "ZXEIGHTYONE": "81_libretro.so", 43 | "NEOGEO": "fbalpha2012_neogeo_libretro.so", 44 | "NEOCD": "neocd_libretro.so", 45 | "PS": "pcsx_rearmed_libretro.so", 46 | "UZEBOX": "uzem_libretro.so", 47 | "LYNX": "handy_libretro.so", 48 | "WS": "mednafen_wswan_libretro.so", 49 | "GW": "gw_libretro.so", 50 | "GB": "gambatte_libretro.so", 51 | "GBA": "mgba_libretro.so", 52 | "GPSP": "gpsp_libretro.so", 53 | "GBC": "gambatte_libretro.so", 54 | "POKE": "pokemini_libretro.so", 55 | "GG": "picodrive_libretro.so", 56 | "NGP": "mednafen_ngp_libretro.so", 57 | "MEGADUCK": "sameduck_libretro.so", 58 | "SUPERVISION": "potator_libretro.so", 59 | "SUFAMI": "snes9x_libretro.so", 60 | "VMU": "vemulator_libretro.so", 61 | "PCECD": "mednafen_pce_fast_libretro.so", 62 | "FDS": "fceumm_libretro.so", 63 | "SATELLAVIEW": "snes9x_libretro.so", 64 | "SGB": "mgba_libretro.so", 65 | "THIRTYTWOX": "picodrive_libretro.so", 66 | "SEGACD": "genesis_plus_gx_libretro.so", 67 | "MSX": "bluemsx_libretro.so", 68 | "PICO": "fake08_libretro.so", 69 | "SCUMMVM": "scummvm_libretro.so", 70 | "TIC": "tic80_libretro.so", 71 | "DOOM": "prboom_libretro.so", 72 | "QUAKE": "tyrquake_libretro.so", 73 | "VIDEOS": "ffmpeg_libretro.so", 74 | "PORTS": "/bin/sh", 75 | "APPS": "/bin/sh" 76 | } 77 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #define VERSION "1.2" 5 | 6 | #define WINDOW_W 640 /* Window width */ 7 | #define WINDOW_H 480 /* Window height */ 8 | #define FPS 30 /* Frames per second */ 9 | #define FRAME_TIME 1000 / FPS /* Duration of 1 frame in ms */ 10 | #define NO_OBJECTS 7 /* Number of objects on screen */ 11 | 12 | #define SYSTEM_NAME_SIZE 20 13 | #define CORE_NAME_SIZE 100 14 | #define NUMBER_OF_FILES 100 15 | #define JSON_BUFFER_SIZE 10000 16 | #define SEARCH_WORD_SIZE 18 17 | 18 | #define SD1_ROMS_PATH "/mnt/mmc/Roms" 19 | #define SD2_ROMS_PATH "/mnt/SDCARD/Roms" 20 | #define COREMAPPING_JSON_PATH "/mnt/mmc/CFW/config/coremapping.json" 21 | #define SETTINGS_JSON_PATH "/mnt/mmc/CFW/skin/settings.json" 22 | #define BACKGROUND_PATH "/mnt/mmc/CFW/skin/background.png" 23 | #define ICONS_PATH "/mnt/mmc/CFW/skin" 24 | #define ROMSEARCH_SH_PATH "/mnt/mmc/CFW/retroarch/romsearch.sh" 25 | #define MAME_FILELIST_PATH "/mnt/mmc/CFW/config/mame.csv" 26 | #define LANGUAGE_FLAG_PATH "/mnt/mmc/CFW/language.flag" 27 | #define LANGUAGES_PATH "/mnt/mmc/CFW/lang" 28 | #define FONTS_PATH "/mnt/mmc/CFW/font" 29 | #define SKIN_PATH "./res/skin.json" 30 | #define DEF_BACKGROUND_PATH "./res/background.png" 31 | #define DEF_FONT_PATH "./res" 32 | #define DEF_FONT "./res/font.otf" 33 | #define DEF_SETTINGS_JSON_PATH "./res/settings.json" 34 | #define DEF_MAME_FILELIST_PATH "./res/mame.csv" 35 | #define DEF_COREMAPPING_JSON_PATH "./res/coremapping.json" 36 | #define DEF_ICONS_PATH "./res" 37 | #define DEF_LANGUAGES_PATH "./res/English.json" 38 | 39 | #define RG35_MENU_CODE 117 /* RG35xx button code */ 40 | #define RG35_UP_CODE 119 /* RG35xx button code */ 41 | #define RG35_RIGHT_CODE 100 /* RG35xx button code */ 42 | #define RG35_DOWN_CODE 115 /* RG35xx button code */ 43 | #define RG35_LEFT_CODE 113 /* RG35xx button code */ 44 | #define RG35_A_CODE 97 /* RG35xx button code */ 45 | #define RG35_B_CODE 98 /* RG35xx button code */ 46 | #define RG35_X_CODE 120 /* RG35xx button code */ 47 | #define RG35_Y_CODE 121 /* RG35xx button code */ 48 | #define RG35_SELECT_CODE 110 /* RG35xx button code */ 49 | #define RG35_START_CODE 109 /* RG35xx button code */ 50 | #define RG35_L1_CODE 104 /* RG35xx button code */ 51 | #define RG35_L2_CODE 106 /* RG35xx button code */ 52 | #define RG35_R1_CODE 108 /* RG35xx button code */ 53 | #define RG35_R2_CODE 107 /* RG35xx button code */ 54 | 55 | #define DEF_C_ACTIVE_TEXT color(255, 255, 255) /* Default active text color (Garlic skin) */ 56 | #define DEF_C_INACTIVE_TEXT color(107, 107, 107) /* Default inactive text color (Garlic skin) */ 57 | #define DEF_C_GUIDE_TEXT color(255, 255, 255) /* Default guide text color (Garlic skin) */ 58 | #define DEF_TEXT_ALIGN "left" 59 | #define DEF_KEYBOARD_ALIGN "left" 60 | #define DEF_TEXT_MARGIN 352 61 | #define DEF_ROMS_FONT_SIZE 20 62 | #define DEF_TEXT_FONT_SIZE 22 63 | #define KEYBOARD_FONT_SIZE 14 64 | #define SEARCH_WORD_FONT_SIZE 20 65 | 66 | #define MAME_CONSOLE_LIST {"ARCADE", "CPS1", "CPS2", "CPS3", "FBNEO", "MAME2000", "NEOGEOCD", "NEOGEO", "FBA2012"} 67 | #define MAME_SYSTEM_NAME_SIZE 10 68 | #define MAME_BUFFER_LINES 40000 69 | #define MAME_BUFFER_TAG_SIZE 30 70 | #define MAME_BUFFER_LONG_SIZE 180 71 | #define MAME_CONSOLE_COUNT 9 72 | 73 | #define KB_ROWS 4 74 | #define KB_COLUMNS 10 75 | #define KB_X 8 76 | #define KB_Y 105 77 | #define KB_W 340 78 | #define KB_H 270 79 | #define SB_W 328 80 | #define SB_H 48 81 | #define KY_W 31 82 | #define KY_H 38 83 | #define KB_FLIP_OFFSET (WINDOW_W - KB_W - 2 * KB_X) 84 | 85 | #define BACKGROUND_C color(30,30,46) /* Default color used if no background is found */ 86 | #define KB_C_BACKGND color(52, 52, 52) /* Default keyboard background color */ 87 | #define KB_C_SB_FRAME color(70, 68, 72) /* Default keyboard search box frame color */ 88 | #define KB_C_SB color(35, 35, 35) /* Default keyboard search box color */ 89 | #define KB_C_KEY color(125, 125, 125) /* Default keyboard key color */ 90 | #define KB_C_KEY_TXT_SEL color(125, 125, 125) /* Default keyboard selected key text color */ 91 | #define KB_C_KEY_TXT color(220, 220, 220) /* Default keyboard unselected key text color */ 92 | #define KB_C_INFO color(125, 125, 125) /* Default keyboard info text color */ 93 | #define KB_C_ST color(220, 220, 220) /* Default keyboard search text color */ 94 | #define KB_C_CURSOR color(220, 220, 220) /* Default keyboard cursor color */ 95 | 96 | #define MENU_BUTTON RG35_MENU_CODE /* Code of the pressed button to exit the application */ 97 | #define MENU_BUTTON_INDEX 0 /* Button config list index of the pressed button to exit the application */ 98 | #define FIRST_PRESS_DELAY 300 99 | #define PRESS_DELAY 50 100 | #define CURSOR_BLINK_DELAY 500 101 | 102 | #define OBJ_MOVE_BT_INDEX 1 103 | #define OBJ_OPEN_BT_INDEX 2 104 | #define OBJ_BACK_BT_INDEX 3 105 | #define OBJ_KEYBOARD_BT_INDEX 4 106 | #define OBJ_SCREENSHOT_INDEX 5 107 | #define OBJ_CONSOLE_INDEX 6 108 | #define OBJECTS_CONFIG \ 109 | {\ 110 | {NULL, { 20, 15, 0, 0}, { 70, 15, 0, 0}, "SEARCH", "logo.png", 1, 1},\ 111 | {NULL, { 20, 420, 0, 0}, { 70, 435, 0, 0}, "MOVE", "icon-dpad-54.png", 1, 1},\ 112 | {NULL, {155, 420, 0, 0}, {205, 435, 0, 0}, "OPEN", "icon-A-54.png", 1, 1},\ 113 | {NULL, {295, 420, 0, 0}, {345, 435, 0, 0}, "BACK", "icon-B-54.png", 1, 1},\ 114 | {NULL, {430, 420, 0, 0}, {480, 435, 0, 0}, "KEYBOARD", "icon-Y-54.png", 1, 1},\ 115 | {NULL, { 0, 0, 0, 0}, { 0, 0, 0, 0}, "IMAGE", "", 1, 0}, /* Screenshots */\ 116 | {NULL, {435, 25, 0, 0}, { 0, 0, 0, 0}, "", "", 1, 0} /* Console */\ 117 | } 118 | 119 | #define KEYBOARD_LAYOUTS \ 120 | {\ 121 | {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"},\ 122 | {"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"},\ 123 | {"A", "S", "D", "F", "G", "H", "J", "K", "L", "-"},\ 124 | {"Z", "X", "C", "V", "B", "N", "M", " ", ".", "_"}\ 125 | } 126 | #endif 127 | -------------------------------------------------------------------------------- /src/cJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) 32 | #define __WINDOWS__ 33 | #endif 34 | 35 | #ifdef __WINDOWS__ 36 | 37 | /* 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: 38 | 39 | CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols 40 | CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) 41 | CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol 42 | 43 | For *nix builds that support visibility attribute, you can define similar behavior by 44 | 45 | setting default visibility to hidden by adding 46 | -fvisibility=hidden (for gcc) 47 | or 48 | -xldscope=hidden (for sun cc) 49 | to CFLAGS 50 | 51 | then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does 52 | 53 | */ 54 | 55 | #define CJSON_CDECL __cdecl 56 | #define CJSON_STDCALL __stdcall 57 | 58 | /* export symbols by default, this is necessary for copy pasting the C and header file */ 59 | #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) 60 | #define CJSON_EXPORT_SYMBOLS 61 | #endif 62 | 63 | #if defined(CJSON_HIDE_SYMBOLS) 64 | #define CJSON_PUBLIC(type) type CJSON_STDCALL 65 | #elif defined(CJSON_EXPORT_SYMBOLS) 66 | #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL 67 | #elif defined(CJSON_IMPORT_SYMBOLS) 68 | #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL 69 | #endif 70 | #else /* !__WINDOWS__ */ 71 | #define CJSON_CDECL 72 | #define CJSON_STDCALL 73 | 74 | #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) 75 | #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type 76 | #else 77 | #define CJSON_PUBLIC(type) type 78 | #endif 79 | #endif 80 | 81 | /* project version */ 82 | #define CJSON_VERSION_MAJOR 1 83 | #define CJSON_VERSION_MINOR 7 84 | #define CJSON_VERSION_PATCH 15 85 | 86 | #include 87 | 88 | /* cJSON Types: */ 89 | #define cJSON_Invalid (0) 90 | #define cJSON_False (1 << 0) 91 | #define cJSON_True (1 << 1) 92 | #define cJSON_NULL (1 << 2) 93 | #define cJSON_Number (1 << 3) 94 | #define cJSON_String (1 << 4) 95 | #define cJSON_Array (1 << 5) 96 | #define cJSON_Object (1 << 6) 97 | #define cJSON_Raw (1 << 7) /* raw json */ 98 | 99 | #define cJSON_IsReference 256 100 | #define cJSON_StringIsConst 512 101 | 102 | /* The cJSON structure: */ 103 | typedef struct cJSON 104 | { 105 | /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 106 | struct cJSON *next; 107 | struct cJSON *prev; 108 | /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 109 | struct cJSON *child; 110 | 111 | /* The type of the item, as above. */ 112 | int type; 113 | 114 | /* The item's string, if type==cJSON_String and type == cJSON_Raw */ 115 | char *valuestring; 116 | /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ 117 | int valueint; 118 | /* The item's number, if type==cJSON_Number */ 119 | double valuedouble; 120 | 121 | /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 122 | char *string; 123 | } cJSON; 124 | 125 | typedef struct cJSON_Hooks 126 | { 127 | /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ 128 | void *(CJSON_CDECL *malloc_fn)(size_t sz); 129 | void (CJSON_CDECL *free_fn)(void *ptr); 130 | } cJSON_Hooks; 131 | 132 | typedef int cJSON_bool; 133 | 134 | /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. 135 | * This is to prevent stack overflows. */ 136 | #ifndef CJSON_NESTING_LIMIT 137 | #define CJSON_NESTING_LIMIT 1000 138 | #endif 139 | 140 | /* returns the version of cJSON as a string */ 141 | CJSON_PUBLIC(const char*) cJSON_Version(void); 142 | 143 | /* Supply malloc, realloc and free functions to cJSON */ 144 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); 145 | 146 | /* 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. */ 147 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ 148 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); 149 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); 150 | /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ 151 | /* 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(). */ 152 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); 153 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); 154 | 155 | /* Render a cJSON entity to text for transfer/storage. */ 156 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); 157 | /* Render a cJSON entity to text for transfer/storage without any formatting. */ 158 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); 159 | /* 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 */ 160 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); 161 | /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ 162 | /* 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 */ 163 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); 164 | /* Delete a cJSON entity and all subentities. */ 165 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); 166 | 167 | /* Returns the number of items in an array (or object). */ 168 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); 169 | /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ 170 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); 171 | /* Get item "string" from object. Case insensitive. */ 172 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); 173 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); 174 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); 175 | /* 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. */ 176 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); 177 | 178 | /* Check item type and return its value */ 179 | CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); 180 | CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); 181 | 182 | /* These functions check the type of an item */ 183 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); 184 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); 185 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); 186 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); 187 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); 188 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); 189 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); 190 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); 191 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); 192 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); 193 | 194 | /* These calls create a cJSON item of the appropriate type. */ 195 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); 196 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); 197 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); 198 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); 199 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); 200 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); 201 | /* raw json */ 202 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); 203 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); 204 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); 205 | 206 | /* Create a string where valuestring references a string so 207 | * it will not be freed by cJSON_Delete */ 208 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); 209 | /* Create an object/array that only references it's elements so 210 | * they will not be freed by cJSON_Delete */ 211 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); 212 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); 213 | 214 | /* These utilities create an Array of count items. 215 | * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ 216 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); 217 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); 218 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); 219 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); 220 | 221 | /* Append item to the specified array/object. */ 222 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); 223 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); 224 | /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. 225 | * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before 226 | * writing to `item->string` */ 227 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); 228 | /* 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. */ 229 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 230 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); 231 | 232 | /* Remove/Detach items from Arrays/Objects. */ 233 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); 234 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); 235 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); 236 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); 237 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); 238 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); 239 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); 240 | 241 | /* Update array items. */ 242 | CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ 243 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); 244 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); 245 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 246 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); 247 | 248 | /* Duplicate a cJSON item */ 249 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); 250 | /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will 251 | * need to be released. With recurse!=0, it will duplicate any children connected to the item. 252 | * The item->next and ->prev pointers are always zero on return from Duplicate. */ 253 | /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. 254 | * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ 255 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); 256 | 257 | /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. 258 | * The input pointer json cannot point to a read-only address area, such as a string constant, 259 | * but should point to a readable and writable address area. */ 260 | CJSON_PUBLIC(void) cJSON_Minify(char *json); 261 | 262 | /* Helper functions for creating and adding items to an object at the same time. 263 | * They return the added item or NULL on failure. */ 264 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); 265 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); 266 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); 267 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); 268 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); 269 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); 270 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); 271 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); 272 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); 273 | 274 | /* When assigning an integer value, it needs to be propagated to valuedouble too. */ 275 | #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) 276 | /* helper for the cJSON_SetNumberValue macro */ 277 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); 278 | #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) 279 | /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ 280 | CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); 281 | 282 | /* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ 283 | #define cJSON_SetBoolValue(object, boolValue) ( \ 284 | (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ 285 | (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ 286 | cJSON_Invalid\ 287 | ) 288 | 289 | /* Macro for iterating over an array or object */ 290 | #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) 291 | 292 | /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ 293 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size); 294 | CJSON_PUBLIC(void) cJSON_free(void *object); 295 | 296 | #ifdef __cplusplus 297 | } 298 | #endif 299 | 300 | #endif 301 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/SDL_rotozoom.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SDL_rotozoom.c 4 | 5 | Copyright (C) A. Schiffler, July 2001 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | 21 | */ 22 | 23 | #ifdef WIN32 24 | #include 25 | #endif 26 | 27 | #include 28 | #include 29 | 30 | #include "SDL_rotozoom.h" 31 | 32 | #define MAX(a,b) (((a) > (b)) ? (a) : (b)) 33 | 34 | /* 35 | 36 | 32bit Zoomer with optional anti-aliasing by bilinear interpolation. 37 | 38 | Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. 39 | 40 | */ 41 | 42 | int 43 | zoomSurfaceRGBA (SDL_Surface * src, SDL_Surface * dst, int smooth) 44 | { 45 | int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep; 46 | tColorRGBA *c00, *c01, *c10, *c11; 47 | tColorRGBA *sp, *csp, *dp; 48 | int sgap, dgap, orderRGBA; 49 | 50 | /* Variable setup */ 51 | if (smooth) 52 | { 53 | /* For interpolation: assume source dimension is one pixel */ 54 | /* smaller to avoid overflow on right and bottom edge. */ 55 | sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w); 56 | sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h); 57 | } 58 | else 59 | { 60 | sx = (int) (65536.0 * (float) src->w / (float) dst->w); 61 | sy = (int) (65536.0 * (float) src->h / (float) dst->h); 62 | } 63 | 64 | /* Allocate memory for row increments */ 65 | if ((sax = (int *) malloc ((dst->w + 1) * sizeof (Uint32))) == NULL) 66 | { 67 | return (-1); 68 | } 69 | if ((say = (int *) malloc ((dst->h + 1) * sizeof (Uint32))) == NULL) 70 | { 71 | free (sax); 72 | return (-1); 73 | } 74 | 75 | /* Precalculate row increments */ 76 | csx = 0; 77 | csax = sax; 78 | for (x = 0; x <= dst->w; x++) 79 | { 80 | *csax = csx; 81 | csax++; 82 | csx &= 0xffff; 83 | csx += sx; 84 | } 85 | csy = 0; 86 | csay = say; 87 | for (y = 0; y <= dst->h; y++) 88 | { 89 | *csay = csy; 90 | csay++; 91 | csy &= 0xffff; 92 | csy += sy; 93 | } 94 | 95 | /* Pointer setup */ 96 | sp = csp = (tColorRGBA *) src->pixels; 97 | dp = (tColorRGBA *) dst->pixels; 98 | sgap = src->pitch - src->w * 4; 99 | dgap = dst->pitch - dst->w * 4; 100 | orderRGBA = (src->format->Rmask == 0x000000ff); 101 | 102 | /* Switch between interpolating and non-interpolating code */ 103 | if (smooth) 104 | { 105 | 106 | /* Interpolating Zoom */ 107 | 108 | /* Scan destination */ 109 | csay = say; 110 | for (y = 0; y < dst->h; y++) 111 | { 112 | /* Setup color source pointers */ 113 | c00 = csp; 114 | c01 = csp; 115 | c01++; 116 | c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch); 117 | c11 = c10; 118 | c11++; 119 | csax = sax; 120 | for (x = 0; x < dst->w; x++) 121 | { 122 | /* ABGR ordering */ 123 | /* Interpolate colors */ 124 | ex = (*csax & 0xffff); 125 | ey = (*csay & 0xffff); 126 | t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; 127 | t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; 128 | dp->r = (((t2 - t1) * ey) >> 16) + t1; 129 | t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; 130 | t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; 131 | dp->g = (((t2 - t1) * ey) >> 16) + t1; 132 | t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; 133 | t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; 134 | dp->b = (((t2 - t1) * ey) >> 16) + t1; 135 | t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; 136 | t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; 137 | dp->a = (((t2 - t1) * ey) >> 16) + t1; 138 | /* Advance source pointers */ 139 | csax++; 140 | sstep = (*csax >> 16); 141 | c00 += sstep; 142 | c01 += sstep; 143 | c10 += sstep; 144 | c11 += sstep; 145 | /* Advance destination pointer */ 146 | dp++; 147 | } 148 | /* Advance source pointer */ 149 | csay++; 150 | csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); 151 | /* Advance destination pointers */ 152 | dp = (tColorRGBA *) ((Uint8 *) dp + dgap); 153 | } 154 | 155 | } 156 | else 157 | { 158 | 159 | /* Non-Interpolating Zoom */ 160 | 161 | csay = say; 162 | for (y = 0; y < dst->h; y++) 163 | { 164 | sp = csp; 165 | csax = sax; 166 | for (x = 0; x < dst->w; x++) 167 | { 168 | /* Draw */ 169 | *dp = *sp; 170 | /* Advance source pointers */ 171 | csax++; 172 | sp += (*csax >> 16); 173 | /* Advance destination pointer */ 174 | dp++; 175 | } 176 | /* Advance source pointer */ 177 | csay++; 178 | csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); 179 | /* Advance destination pointers */ 180 | dp = (tColorRGBA *) ((Uint8 *) dp + dgap); 181 | } 182 | 183 | } 184 | 185 | /* Remove temp arrays */ 186 | free (sax); 187 | free (say); 188 | 189 | return (0); 190 | } 191 | 192 | /* 193 | 194 | 8bit Zoomer without smoothing. 195 | 196 | Zoomes 8bit palette/Y 'src' surface to 'dst' surface. 197 | 198 | */ 199 | 200 | int 201 | zoomSurfaceY (SDL_Surface * src, SDL_Surface * dst) 202 | { 203 | Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy; 204 | Uint8 *sp, *dp, *csp; 205 | int dgap; 206 | 207 | /* Variable setup */ 208 | sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w); 209 | sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h); 210 | 211 | /* Allocate memory for row increments */ 212 | if ((sax = (Uint32 *) malloc (dst->w * sizeof (Uint32))) == NULL) 213 | { 214 | return (-1); 215 | } 216 | if ((say = (Uint32 *) malloc (dst->h * sizeof (Uint32))) == NULL) 217 | { 218 | if (sax != NULL) 219 | { 220 | free (sax); 221 | } 222 | return (-1); 223 | } 224 | 225 | /* Precalculate row increments */ 226 | csx = 0; 227 | csax = sax; 228 | for (x = 0; x < dst->w; x++) 229 | { 230 | csx += sx; 231 | *csax = (csx >> 16); 232 | csx &= 0xffff; 233 | csax++; 234 | } 235 | csy = 0; 236 | csay = say; 237 | for (y = 0; y < dst->h; y++) 238 | { 239 | csy += sy; 240 | *csay = (csy >> 16); 241 | csy &= 0xffff; 242 | csay++; 243 | } 244 | 245 | csx = 0; 246 | csax = sax; 247 | for (x = 0; x < dst->w; x++) 248 | { 249 | csx += (*csax); 250 | csax++; 251 | } 252 | csy = 0; 253 | csay = say; 254 | for (y = 0; y < dst->h; y++) 255 | { 256 | csy += (*csay); 257 | csay++; 258 | } 259 | 260 | /* Pointer setup */ 261 | sp = csp = (Uint8 *) src->pixels; 262 | dp = (Uint8 *) dst->pixels; 263 | dgap = dst->pitch - dst->w; 264 | 265 | /* Draw */ 266 | csay = say; 267 | for (y = 0; y < dst->h; y++) 268 | { 269 | csax = sax; 270 | sp = csp; 271 | for (x = 0; x < dst->w; x++) 272 | { 273 | /* Draw */ 274 | *dp = *sp; 275 | /* Advance source pointers */ 276 | sp += (*csax); 277 | csax++; 278 | /* Advance destination pointer */ 279 | dp++; 280 | } 281 | /* Advance source pointer (for row) */ 282 | csp += ((*csay) * src->pitch); 283 | csay++; 284 | /* Advance destination pointers */ 285 | dp += dgap; 286 | } 287 | 288 | /* Remove temp arrays */ 289 | free (sax); 290 | free (say); 291 | 292 | return (0); 293 | } 294 | 295 | /* 296 | 297 | 32bit Rotozoomer with optional anti-aliasing by bilinear interpolation. 298 | 299 | Rotates and zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. 300 | 301 | */ 302 | 303 | void 304 | transformSurfaceRGBA (SDL_Surface * src, SDL_Surface * dst, int cx, int cy, 305 | int isin, int icos, int smooth) 306 | { 307 | int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh; 308 | tColorRGBA c00, c01, c10, c11; 309 | tColorRGBA *pc, *sp; 310 | int gap, orderRGBA; 311 | 312 | /* Variable setup */ 313 | xd = ((src->w - dst->w) << 15); 314 | yd = ((src->h - dst->h) << 15); 315 | ax = (cx << 16) - (icos * cx); 316 | ay = (cy << 16) - (isin * cx); 317 | sw = src->w - 1; 318 | sh = src->h - 1; 319 | pc = (tColorRGBA*)dst->pixels; 320 | gap = dst->pitch - dst->w * 4; 321 | orderRGBA = (src->format->Rmask == 0x000000ff); 322 | 323 | /* Switch between interpolating and non-interpolating code */ 324 | if (smooth) 325 | { 326 | for (y = 0; y < dst->h; y++) 327 | { 328 | dy = cy - y; 329 | sdx = (ax + (isin * dy)) + xd; 330 | sdy = (ay - (icos * dy)) + yd; 331 | for (x = 0; x < dst->w; x++) 332 | { 333 | dx = (sdx >> 16); 334 | dy = (sdy >> 16); 335 | if ((dx >= -1) && (dy >= -1) && (dx < src->w) && (dy < src->h)) 336 | { 337 | if ((dx >= 0) && (dy >= 0) && (dx < sw) && (dy < sh)) 338 | { 339 | sp = 340 | (tColorRGBA *) ((Uint8 *) src->pixels + 341 | src->pitch * dy); 342 | sp += dx; 343 | c00 = *sp; 344 | sp += 1; 345 | c01 = *sp; 346 | sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); 347 | sp -= 1; 348 | c10 = *sp; 349 | sp += 1; 350 | c11 = *sp; 351 | } 352 | else if ((dx == sw) && (dy == sh)) 353 | { 354 | sp = 355 | (tColorRGBA *) ((Uint8 *) src->pixels + 356 | src->pitch * dy); 357 | sp += dx; 358 | c00 = *sp; 359 | c01 = *pc; 360 | c10 = *pc; 361 | c11 = *pc; 362 | } 363 | else if ((dx == -1) && (dy == -1)) 364 | { 365 | sp = (tColorRGBA *) (src->pixels); 366 | c00 = *pc; 367 | c01 = *pc; 368 | c10 = *pc; 369 | c11 = *sp; 370 | } 371 | else if ((dx == -1) && (dy == sh)) 372 | { 373 | sp = (tColorRGBA *) (src->pixels); 374 | sp = 375 | (tColorRGBA *) ((Uint8 *) src->pixels + 376 | src->pitch * dy); 377 | c00 = *pc; 378 | c01 = *sp; 379 | c10 = *pc; 380 | c11 = *pc; 381 | } 382 | else if ((dx == sw) && (dy == -1)) 383 | { 384 | sp = (tColorRGBA *) (src->pixels); 385 | sp += dx; 386 | c00 = *pc; 387 | c01 = *pc; 388 | c10 = *sp; 389 | c11 = *pc; 390 | } 391 | else if (dx == -1) 392 | { 393 | sp = 394 | (tColorRGBA *) ((Uint8 *) src->pixels + 395 | src->pitch * dy); 396 | c00 = *pc; 397 | c01 = *sp; 398 | c10 = *pc; 399 | sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); 400 | c11 = *sp; 401 | } 402 | else if (dy == -1) 403 | { 404 | sp = (tColorRGBA *) (src->pixels); 405 | sp += dx; 406 | c00 = *pc; 407 | c01 = *pc; 408 | c10 = *sp; 409 | sp += 1; 410 | c11 = *sp; 411 | } 412 | else if (dx == sw) 413 | { 414 | sp = 415 | (tColorRGBA *) ((Uint8 *) src->pixels + 416 | src->pitch * dy); 417 | sp += dx; 418 | c00 = *sp; 419 | c01 = *pc; 420 | sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); 421 | c10 = *sp; 422 | c11 = *pc; 423 | } 424 | else if (dy == sh) 425 | { 426 | sp = 427 | (tColorRGBA *) ((Uint8 *) src->pixels + 428 | src->pitch * dy); 429 | sp += dx; 430 | c00 = *sp; 431 | sp += 1; 432 | c01 = *sp; 433 | c10 = *pc; 434 | c11 = *pc; 435 | } 436 | /* Interpolate colors */ 437 | ex = (sdx & 0xffff); 438 | ey = (sdy & 0xffff); 439 | t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; 440 | t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; 441 | pc->r = (((t2 - t1) * ey) >> 16) + t1; 442 | t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; 443 | t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; 444 | pc->g = (((t2 - t1) * ey) >> 16) + t1; 445 | t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; 446 | t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; 447 | pc->b = (((t2 - t1) * ey) >> 16) + t1; 448 | t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; 449 | t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; 450 | pc->a = (((t2 - t1) * ey) >> 16) + t1; 451 | 452 | } 453 | sdx += icos; 454 | sdy += isin; 455 | pc++; 456 | } 457 | pc = (tColorRGBA *) ((Uint8 *) pc + gap); 458 | } 459 | } 460 | else 461 | { 462 | for (y = 0; y < dst->h; y++) 463 | { 464 | dy = cy - y; 465 | sdx = (ax + (isin * dy)) + xd; 466 | sdy = (ay - (icos * dy)) + yd; 467 | for (x = 0; x < dst->w; x++) 468 | { 469 | dx = (short) (sdx >> 16); 470 | dy = (short) (sdy >> 16); 471 | if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) 472 | { 473 | sp = 474 | (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); 475 | sp += dx; 476 | *pc = *sp; 477 | } 478 | sdx += icos; 479 | sdy += isin; 480 | pc++; 481 | } 482 | pc = (tColorRGBA *) ((Uint8 *) pc + gap); 483 | } 484 | } 485 | } 486 | 487 | /* 488 | 489 | 8bit Rotozoomer without smoothing 490 | 491 | Rotates and zoomes 8bit palette/Y 'src' surface to 'dst' surface. 492 | 493 | */ 494 | 495 | void 496 | transformSurfaceY (SDL_Surface * src, SDL_Surface * dst, int cx, int cy, 497 | int isin, int icos) 498 | { 499 | int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh; 500 | tColorY *pc, *sp; 501 | int gap; 502 | 503 | /* Variable setup */ 504 | xd = ((src->w - dst->w) << 15); 505 | yd = ((src->h - dst->h) << 15); 506 | ax = (cx << 16) - (icos * cx); 507 | ay = (cy << 16) - (isin * cx); 508 | sw = src->w - 1; 509 | sh = src->h - 1; 510 | pc = (tColorY*)dst->pixels; 511 | gap = dst->pitch - dst->w; 512 | /* Clear surface to colorkey */ 513 | memset (pc, (unsigned char) (src->format->colorkey & 0xff), 514 | dst->pitch * dst->h); 515 | /* Iterate through destination surface */ 516 | for (y = 0; y < dst->h; y++) 517 | { 518 | dy = cy - y; 519 | sdx = (ax + (isin * dy)) + xd; 520 | sdy = (ay - (icos * dy)) + yd; 521 | for (x = 0; x < dst->w; x++) 522 | { 523 | dx = (short) (sdx >> 16); 524 | dy = (short) (sdy >> 16); 525 | if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) 526 | { 527 | sp = (tColorY *) (src->pixels); 528 | sp += (src->pitch * dy + dx); 529 | *pc = *sp; 530 | } 531 | sdx += icos; 532 | sdy += isin; 533 | pc++; 534 | } 535 | pc += gap; 536 | } 537 | } 538 | 539 | /* 540 | 541 | rotozoomSurface() 542 | 543 | Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. 544 | 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1 545 | then the destination 32bit surface is anti-aliased. If the surface is not 8bit 546 | or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. 547 | 548 | */ 549 | 550 | #define VALUE_LIMIT 0.001 551 | 552 | SDL_Surface * 553 | rotozoomSurface (SDL_Surface * src, double angle, double zoom, int smooth) 554 | { 555 | SDL_Surface *rz_src; 556 | SDL_Surface *rz_dst; 557 | double zoominv; 558 | double radangle, sanglezoom, canglezoom, sanglezoominv, canglezoominv; 559 | int dstwidthhalf, dstwidth, dstheighthalf, dstheight; 560 | double x, y, cx, cy, sx, sy; 561 | int is32bit; 562 | int i, src_converted; 563 | 564 | /* Sanity check */ 565 | if (src == NULL) 566 | return (NULL); 567 | 568 | /* Determine if source surface is 32bit or 8bit */ 569 | is32bit = (src->format->BitsPerPixel == 32); 570 | if ((is32bit) || (src->format->BitsPerPixel == 8)) 571 | { 572 | /* Use source surface 'as is' */ 573 | rz_src = src; 574 | src_converted = 0; 575 | } 576 | else 577 | { 578 | /* New source surface is 32bit with a defined RGBA ordering */ 579 | rz_src = 580 | SDL_CreateRGBSurface (SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 581 | 0x0000ff00, 0x00ff0000, 0xff000000); 582 | SDL_BlitSurface (src, NULL, rz_src, NULL); 583 | src_converted = 1; 584 | is32bit = 1; 585 | } 586 | 587 | /* Sanity check zoom factor */ 588 | if (zoom < VALUE_LIMIT) 589 | { 590 | zoom = VALUE_LIMIT; 591 | } 592 | zoominv = 65536.0 / zoom; 593 | 594 | /* Check if we have a rotozoom or just a zoom */ 595 | if (fabs (angle) > VALUE_LIMIT) 596 | { 597 | 598 | /* Angle!=0: full rotozoom */ 599 | /* ----------------------- */ 600 | 601 | /* Calculate target factors from sin/cos and zoom */ 602 | radangle = angle * (M_PI / 180.0); 603 | sanglezoom = sanglezoominv = sin (radangle); 604 | canglezoom = canglezoominv = cos (radangle); 605 | sanglezoom *= zoom; 606 | canglezoom *= zoom; 607 | sanglezoominv *= zoominv; 608 | canglezoominv *= zoominv; 609 | 610 | /* Determine destination width and height by rotating a centered source box */ 611 | x = rz_src->w / 2; 612 | y = rz_src->h / 2; 613 | cx = canglezoom * x; 614 | cy = canglezoom * y; 615 | sx = sanglezoom * x; 616 | sy = sanglezoom * y; 617 | dstwidthhalf = 618 | MAX ((int) 619 | ceil (MAX 620 | (MAX 621 | (MAX (fabs (cx + sy), fabs (cx - sy)), fabs (-cx + sy)), 622 | fabs (-cx - sy))), 1); 623 | dstheighthalf = 624 | MAX ((int) 625 | ceil (MAX 626 | (MAX 627 | (MAX (fabs (sx + cy), fabs (sx - cy)), fabs (-sx + cy)), 628 | fabs (-sx - cy))), 1); 629 | dstwidth = 2 * dstwidthhalf; 630 | dstheight = 2 * dstheighthalf; 631 | 632 | /* Alloc space to completely contain the rotated surface */ 633 | rz_dst = NULL; 634 | if (is32bit) 635 | { 636 | /* Target surface is 32bit with source RGBA/ABGR ordering */ 637 | rz_dst = 638 | SDL_CreateRGBSurface (SDL_SWSURFACE, dstwidth, dstheight, 32, 639 | rz_src->format->Rmask, 640 | rz_src->format->Gmask, 641 | rz_src->format->Bmask, 642 | rz_src->format->Amask); 643 | } 644 | else 645 | { 646 | /* Target surface is 8bit */ 647 | rz_dst = 648 | SDL_CreateRGBSurface (SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 649 | 0, 0); 650 | } 651 | 652 | /* Lock source surface */ 653 | SDL_LockSurface (rz_src); 654 | /* Check which kind of surface we have */ 655 | if (is32bit) 656 | { 657 | /* Call the 32bit transformation routine to do the rotation (using alpha) */ 658 | transformSurfaceRGBA (rz_src, rz_dst, dstwidthhalf, dstheighthalf, 659 | (int) (sanglezoominv), 660 | (int) (canglezoominv), smooth); 661 | /* Turn on source-alpha support */ 662 | SDL_SetAlpha (rz_dst, SDL_SRCALPHA, 255); 663 | } 664 | else 665 | { 666 | /* Copy palette and colorkey info */ 667 | for (i = 0; i < rz_src->format->palette->ncolors; i++) 668 | { 669 | rz_dst->format->palette->colors[i] = 670 | rz_src->format->palette->colors[i]; 671 | } 672 | rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; 673 | /* Call the 8bit transformation routine to do the rotation */ 674 | transformSurfaceY (rz_src, rz_dst, dstwidthhalf, dstheighthalf, 675 | (int) (sanglezoominv), (int) (canglezoominv)); 676 | SDL_SetColorKey (rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, 677 | rz_src->format->colorkey); 678 | } 679 | /* Unlock source surface */ 680 | SDL_UnlockSurface (rz_src); 681 | 682 | } 683 | else 684 | { 685 | 686 | /* Angle=0: Just a zoom */ 687 | /* -------------------- */ 688 | 689 | /* Calculate target size and set rect */ 690 | dstwidth = (int) ((double) rz_src->w * zoom); 691 | dstheight = (int) ((double) rz_src->h * zoom); 692 | if (dstwidth < 1) 693 | { 694 | dstwidth = 1; 695 | } 696 | if (dstheight < 1) 697 | { 698 | dstheight = 1; 699 | } 700 | 701 | /* Alloc space to completely contain the zoomed surface */ 702 | rz_dst = NULL; 703 | if (is32bit) 704 | { 705 | /* Target surface is 32bit with source RGBA/ABGR ordering */ 706 | rz_dst = 707 | SDL_CreateRGBSurface (SDL_SWSURFACE, dstwidth, dstheight, 32, 708 | rz_src->format->Rmask, 709 | rz_src->format->Gmask, 710 | rz_src->format->Bmask, 711 | rz_src->format->Amask); 712 | } 713 | else 714 | { 715 | /* Target surface is 8bit */ 716 | rz_dst = 717 | SDL_CreateRGBSurface (SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 718 | 0, 0); 719 | } 720 | 721 | /* Lock source surface */ 722 | SDL_LockSurface (rz_src); 723 | /* Check which kind of surface we have */ 724 | if (is32bit) 725 | { 726 | /* Call the 32bit transformation routine to do the zooming (using alpha) */ 727 | zoomSurfaceRGBA (rz_src, rz_dst, smooth); 728 | /* Turn on source-alpha support */ 729 | SDL_SetAlpha (rz_dst, SDL_SRCALPHA, 255); 730 | } 731 | else 732 | { 733 | /* Copy palette and colorkey info */ 734 | for (i = 0; i < rz_src->format->palette->ncolors; i++) 735 | { 736 | rz_dst->format->palette->colors[i] = 737 | rz_src->format->palette->colors[i]; 738 | } 739 | rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; 740 | /* Call the 8bit transformation routine to do the zooming */ 741 | zoomSurfaceY (rz_src, rz_dst); 742 | SDL_SetColorKey (rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, 743 | rz_src->format->colorkey); 744 | } 745 | /* Unlock source surface */ 746 | SDL_UnlockSurface (rz_src); 747 | } 748 | 749 | /* Cleanup temp surface */ 750 | if (src_converted) 751 | { 752 | SDL_FreeSurface (rz_src); 753 | } 754 | 755 | /* Return destination surface */ 756 | return (rz_dst); 757 | } 758 | 759 | /* 760 | 761 | zoomSurface() 762 | 763 | Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. 764 | 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1 765 | then the destination 32bit surface is anti-aliased. If the surface is not 8bit 766 | or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. 767 | 768 | */ 769 | 770 | #define VALUE_LIMIT 0.001 771 | 772 | SDL_Surface * 773 | zoomSurface (SDL_Surface * src, double zoomx, double zoomy, int smooth) 774 | { 775 | SDL_Surface *rz_src; 776 | SDL_Surface *rz_dst; 777 | int dstwidth, dstheight; 778 | int is32bit; 779 | int i, src_converted; 780 | 781 | /* Sanity check */ 782 | if (src == NULL) 783 | return (NULL); 784 | 785 | /* Determine if source surface is 32bit or 8bit */ 786 | is32bit = (src->format->BitsPerPixel == 32); 787 | if ((is32bit) || (src->format->BitsPerPixel == 8)) 788 | { 789 | /* Use source surface 'as is' */ 790 | rz_src = src; 791 | src_converted = 0; 792 | } 793 | else 794 | { 795 | /* New source surface is 32bit with a defined RGBA ordering */ 796 | rz_src = 797 | SDL_CreateRGBSurface (SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 798 | 0x0000ff00, 0x00ff0000, 0xff000000); 799 | SDL_BlitSurface (src, NULL, rz_src, NULL); 800 | src_converted = 1; 801 | is32bit = 1; 802 | } 803 | 804 | /* Sanity check zoom factors */ 805 | if (zoomx < VALUE_LIMIT) 806 | { 807 | zoomx = VALUE_LIMIT; 808 | } 809 | if (zoomy < VALUE_LIMIT) 810 | { 811 | zoomy = VALUE_LIMIT; 812 | } 813 | 814 | /* Calculate target size and set rect */ 815 | dstwidth = (int) ((double) rz_src->w * zoomx); 816 | dstheight = (int) ((double) rz_src->h * zoomy); 817 | if (dstwidth < 1) 818 | { 819 | dstwidth = 1; 820 | } 821 | if (dstheight < 1) 822 | { 823 | dstheight = 1; 824 | } 825 | 826 | /* Alloc space to completely contain the zoomed surface */ 827 | rz_dst = NULL; 828 | if (is32bit) 829 | { 830 | /* Target surface is 32bit with source RGBA/ABGR ordering */ 831 | rz_dst = 832 | SDL_CreateRGBSurface (SDL_SWSURFACE, dstwidth, dstheight, 32, 833 | rz_src->format->Rmask, rz_src->format->Gmask, 834 | rz_src->format->Bmask, rz_src->format->Amask); 835 | } 836 | else 837 | { 838 | /* Target surface is 8bit */ 839 | rz_dst = 840 | SDL_CreateRGBSurface (SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 841 | 0); 842 | } 843 | 844 | /* Lock source surface */ 845 | SDL_LockSurface (rz_src); 846 | /* Check which kind of surface we have */ 847 | if (is32bit) 848 | { 849 | /* Call the 32bit transformation routine to do the zooming (using alpha) */ 850 | zoomSurfaceRGBA (rz_src, rz_dst, smooth); 851 | /* Turn on source-alpha support */ 852 | SDL_SetAlpha (rz_dst, SDL_SRCALPHA, 255); 853 | } 854 | else 855 | { 856 | /* Copy palette and colorkey info */ 857 | for (i = 0; i < rz_src->format->palette->ncolors; i++) 858 | { 859 | rz_dst->format->palette->colors[i] = 860 | rz_src->format->palette->colors[i]; 861 | } 862 | rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; 863 | /* Call the 8bit transformation routine to do the zooming */ 864 | zoomSurfaceY (rz_src, rz_dst); 865 | SDL_SetColorKey (rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, 866 | rz_src->format->colorkey); 867 | } 868 | /* Unlock source surface */ 869 | SDL_UnlockSurface (rz_src); 870 | 871 | /* Cleanup temp surface */ 872 | if (src_converted) 873 | { 874 | SDL_FreeSurface (rz_src); 875 | } 876 | 877 | /* Return destination surface */ 878 | return (rz_dst); 879 | } 880 | 881 | #ifdef WIN32 882 | /* For DLL building under VC6 */ 883 | BOOL APIENTRY 884 | DllMain (HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 885 | { 886 | switch (ul_reason_for_call) 887 | { 888 | case DLL_PROCESS_ATTACH: 889 | case DLL_THREAD_ATTACH: 890 | case DLL_THREAD_DETACH: 891 | case DLL_PROCESS_DETACH: 892 | break; 893 | } 894 | return TRUE; 895 | } 896 | #endif 897 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "SDL_rotozoom.h" 9 | #include "config.h" 10 | #include "cJSON.h" 11 | 12 | typedef struct Object_Tag { 13 | SDL_Surface * img; /* Unselected image */ 14 | SDL_Rect imgPos; /* Position of the button image on the screen */ 15 | SDL_Rect textPos; /* Position of the text under the button */ 16 | char name[20]; /* Image text */ 17 | char imgFile[PATH_MAX + 1]; /* PNG File */ 18 | uint8_t imgVisible; /* Image visible */ 19 | uint8_t textVisible; /* Text visible */ 20 | }Object; 21 | 22 | typedef struct Skin_Tag { 23 | SDL_Color background; 24 | SDL_Color sbFrame; 25 | SDL_Color sb; 26 | SDL_Color key; 27 | SDL_Color keySelTxt; 28 | SDL_Color keyTxt; 29 | SDL_Color info; 30 | SDL_Color st; 31 | SDL_Color cursor; 32 | }Skin; 33 | 34 | typedef struct Rom_Tag { 35 | char system[SYSTEM_NAME_SIZE]; 36 | char displayName[PATH_MAX + 1]; 37 | char fileName[PATH_MAX + 1]; 38 | uint8_t romLocation; 39 | }Rom; 40 | 41 | Skin skin; 42 | Rom romsList[NUMBER_OF_FILES]; 43 | char jsonFileBuffer[JSON_BUFFER_SIZE]; 44 | char *keyboardLayout[KB_ROWS][KB_COLUMNS] = KEYBOARD_LAYOUTS; 45 | Object objects[NO_OBJECTS] = OBJECTS_CONFIG; 46 | uint16_t pressDelay = FIRST_PRESS_DELAY; 47 | uint8_t running = 1; 48 | uint8_t keyboardShown = 1; 49 | uint32_t keyDownTime = 0; 50 | uint8_t romsFound = 0; 51 | uint8_t selRomIdx = 0; /* Selected ROM index */ 52 | uint8_t selRomIdxOld = 0; /* Used to detect changes */ 53 | uint8_t selKeyIIdx = 0; /* Selected key i index */ 54 | uint8_t selKeyJIdx = 0; /* Selected key j index */ 55 | char romCoreMapping[CORE_NAME_SIZE]; 56 | char searchWord[SEARCH_WORD_SIZE]; 57 | char textAlign[10]; 58 | SDL_Surface *screen, *background, *textSurface; 59 | SDL_Rect backgroundLocation, textLocation; 60 | TTF_Font *textFont, *romsFont, *keyboardFont, *searchWordFont; 61 | uint16_t textMargin; 62 | SDL_Event event; 63 | cJSON *json; 64 | uint8_t startSearch; 65 | int cursorOld; 66 | SDL_Color activeColor, inactiveColor, guideColor; 67 | int romsFontSize; 68 | int textFontSize; 69 | int kbPosY; 70 | char mamelist[MAME_CONSOLE_COUNT][MAME_SYSTEM_NAME_SIZE] = MAME_CONSOLE_LIST; 71 | char mameFileBufferTag[MAME_BUFFER_LINES][MAME_BUFFER_TAG_SIZE]; 72 | char mameFileBufferLong[MAME_BUFFER_LINES][MAME_BUFFER_LONG_SIZE]; 73 | unsigned int mameFileSize = 0; 74 | char keyboardAlign[6]; 75 | uint8_t updateScreenshot = 0; 76 | char languagePath[PATH_MAX+1] = "<>"; 77 | char font[100]; 78 | int hideHelper = 0; 79 | 80 | 81 | SDL_Rect rect(int x, int y, int w, int h) { return (SDL_Rect){x, y, w, h}; } 82 | SDL_Color color(int r, int g, int b) { return (SDL_Color){r,g,b}; } 83 | 84 | int loadSurface(SDL_Surface **surf, char * image) { 85 | *surf = IMG_Load(image); 86 | return (surf == NULL) ? 0 : 1; 87 | } 88 | 89 | void freeSurface(SDL_Surface **surf) { 90 | if (*surf != NULL) SDL_FreeSurface(*surf); 91 | *surf = NULL; 92 | } 93 | 94 | void drawRect(SDL_Rect pos, int o, SDL_Color c, char * align) { 95 | SDL_Rect invPos = rect(pos.x + o, pos.y, pos.w, pos.h); 96 | Uint32 mapColor = SDL_MapRGB(screen->format, c.r, c.g, c.b); 97 | if (strcmp(align, "right") == 0) SDL_FillRect(screen, &invPos, mapColor); 98 | else SDL_FillRect(screen, &pos, mapColor); 99 | } 100 | 101 | void drawText(SDL_Rect pos, int o, char * text, TTF_Font *font, SDL_Color c, char * align, int from) { 102 | SDL_Rect invPos = rect(pos.x + o, pos.y, pos.w, pos.h); 103 | textSurface = TTF_RenderUTF8_Blended(font, text, c); 104 | if (strcmp(text, "") != 0) { 105 | if (textSurface != NULL) { 106 | if (strcmp(align, "right") == 0) SDL_BlitSurface(textSurface, NULL, screen, &invPos); 107 | else if (strcmp(align, "left") == 0) SDL_BlitSurface(textSurface, NULL, screen, &pos); 108 | else { 109 | SDL_Rect center = rect((pos.x + invPos.x) / 2, pos.y, pos.w, pos.h); 110 | SDL_BlitSurface(textSurface, NULL, screen, ¢er); 111 | } 112 | freeSurface(&textSurface); 113 | } 114 | else fprintf(stderr, "Failed to draw text: %s From: %d\n", text, from); 115 | } 116 | } 117 | 118 | void drawKey(int x, int y, int o, char * text, SDL_Color c) { 119 | int fw, fh; 120 | drawRect(rect(x, y, KY_W, KY_H), o, skin.key, keyboardAlign); 121 | TTF_SizeUTF8(keyboardFont, text, &fw, &fh); 122 | drawText(rect(x + (KY_W - fw) / 2, y + (KY_H - fh)/2, 0, 0), o, text, keyboardFont, c, keyboardAlign, 0); 123 | } 124 | 125 | int loadSurfaceFullScreen(SDL_Surface ** surf, char * image) { 126 | SDL_Surface * tmp = IMG_Load(image); 127 | double wf = 1.0; 128 | double hf = 1.0; 129 | if ((tmp != NULL)) { 130 | wf = (double)WINDOW_W/tmp->w; 131 | hf = (double)WINDOW_H/tmp->h; 132 | *surf = zoomSurface(tmp, wf, hf, 0); 133 | } 134 | else return 0; 135 | SDL_FreeSurface(tmp); 136 | return 1; 137 | } 138 | 139 | void searchMameRomFast(char *fileName) { 140 | int l = 0; 141 | int h = mameFileSize - 1; 142 | int m; 143 | int res; 144 | 145 | while (l <= h) { 146 | m = (l + h) / 2; 147 | res = strcmp(&mameFileBufferTag[m][0], fileName); 148 | if (res == 0) { 149 | strcpy(romsList[romsFound].displayName, &mameFileBufferLong[m][0]); 150 | romsList[romsFound].displayName[strcspn(romsList[romsFound].displayName, "\r\n")] = '\0'; 151 | break; 152 | } 153 | if (res < 0) l = m + 1; 154 | else h = m - 1; 155 | } 156 | } 157 | 158 | SDL_Color getColorFromJson(char * colorTag, SDL_Color defCol) { 159 | SDL_Color color; 160 | cJSON *name = cJSON_GetObjectItemCaseSensitive(json, colorTag); 161 | if (cJSON_IsString(name) != 0) sscanf(name->valuestring,"#%02x%02x%02x",&color.r, &color.g, &color.b); 162 | else { 163 | fprintf(stderr,"%s not found in the json file.\n", colorTag); 164 | return defCol; 165 | } 166 | return color; 167 | } 168 | 169 | void initButtonGuide(void) { 170 | int total = 20; 171 | int pos = 10; 172 | int fw, fh, gap; 173 | 174 | TTF_SizeUTF8(textFont, objects[OBJ_MOVE_BT_INDEX].name, &fw, &fh); 175 | objects[OBJ_MOVE_BT_INDEX].textPos.w = fw; 176 | objects[OBJ_MOVE_BT_INDEX].imgPos.y = WINDOW_H - objects[OBJ_MOVE_BT_INDEX].img->h - 10; 177 | objects[OBJ_MOVE_BT_INDEX].textPos.y = objects[OBJ_MOVE_BT_INDEX].imgPos.y + (objects[OBJ_MOVE_BT_INDEX].img->h - fh) / 2; 178 | total += objects[OBJ_MOVE_BT_INDEX].img->w + fw + 5; 179 | TTF_SizeUTF8(textFont, objects[OBJ_OPEN_BT_INDEX].name, &fw, &fh); 180 | objects[OBJ_OPEN_BT_INDEX].textPos.w = fw; 181 | objects[OBJ_OPEN_BT_INDEX].imgPos.y = WINDOW_H - objects[OBJ_OPEN_BT_INDEX].img->h - 10; 182 | objects[OBJ_OPEN_BT_INDEX].textPos.y = objects[OBJ_MOVE_BT_INDEX].imgPos.y + (objects[OBJ_MOVE_BT_INDEX].img->h - fh) / 2; 183 | total += objects[OBJ_OPEN_BT_INDEX].img->w + fw + 5; 184 | TTF_SizeUTF8(textFont, objects[OBJ_BACK_BT_INDEX].name, &fw, &fh); 185 | objects[OBJ_BACK_BT_INDEX].textPos.w = fw; 186 | objects[OBJ_BACK_BT_INDEX].imgPos.y = WINDOW_H - objects[OBJ_BACK_BT_INDEX].img->h - 10; 187 | objects[OBJ_BACK_BT_INDEX].textPos.y = objects[OBJ_MOVE_BT_INDEX].imgPos.y + (objects[OBJ_MOVE_BT_INDEX].img->h - fh) / 2; 188 | total += objects[OBJ_BACK_BT_INDEX].img->w + fw + 5; 189 | TTF_SizeUTF8(textFont, objects[OBJ_KEYBOARD_BT_INDEX].name, &fw, &fh); 190 | objects[OBJ_KEYBOARD_BT_INDEX].textPos.w = fw; 191 | objects[OBJ_KEYBOARD_BT_INDEX].imgPos.y = WINDOW_H - objects[OBJ_KEYBOARD_BT_INDEX].img->h - 10; 192 | objects[OBJ_KEYBOARD_BT_INDEX].textPos.y = objects[OBJ_MOVE_BT_INDEX].imgPos.y + (objects[OBJ_MOVE_BT_INDEX].img->h - fh) / 2; 193 | total += objects[OBJ_KEYBOARD_BT_INDEX].img->w + fw + 5; 194 | gap = (WINDOW_W - total) / 3; 195 | objects[OBJ_MOVE_BT_INDEX].imgPos.x = pos; 196 | pos += objects[OBJ_MOVE_BT_INDEX].img->w; 197 | objects[OBJ_MOVE_BT_INDEX].textPos.x = pos; 198 | pos += objects[OBJ_MOVE_BT_INDEX].textPos.w + gap; 199 | objects[OBJ_OPEN_BT_INDEX].imgPos.x = pos; 200 | pos += objects[OBJ_OPEN_BT_INDEX].img->w; 201 | objects[OBJ_OPEN_BT_INDEX].textPos.x = pos; 202 | pos += objects[OBJ_OPEN_BT_INDEX].textPos.w + gap; 203 | objects[OBJ_BACK_BT_INDEX].imgPos.x = pos; 204 | pos += objects[OBJ_BACK_BT_INDEX].img->w; 205 | objects[OBJ_BACK_BT_INDEX].textPos.x = pos; 206 | pos += objects[OBJ_BACK_BT_INDEX].textPos.w + gap; 207 | objects[OBJ_KEYBOARD_BT_INDEX].imgPos.x = pos; 208 | pos += objects[OBJ_KEYBOARD_BT_INDEX].img->w; 209 | objects[OBJ_KEYBOARD_BT_INDEX].textPos.x = pos; 210 | pos += objects[OBJ_KEYBOARD_BT_INDEX].textPos.w + gap; 211 | if (hideHelper != 0) { 212 | objects[OBJ_MOVE_BT_INDEX].imgVisible = 0; 213 | objects[OBJ_MOVE_BT_INDEX].textVisible = 0; 214 | objects[OBJ_OPEN_BT_INDEX].imgVisible = 0; 215 | objects[OBJ_OPEN_BT_INDEX].textVisible = 0; 216 | objects[OBJ_BACK_BT_INDEX].imgVisible = 0; 217 | objects[OBJ_BACK_BT_INDEX].textVisible = 0; 218 | objects[OBJ_KEYBOARD_BT_INDEX].imgVisible = 0; 219 | objects[OBJ_KEYBOARD_BT_INDEX].textVisible = 0; 220 | } 221 | } 222 | 223 | void initObjects(void) { 224 | char path[PATH_MAX + 1]; 225 | for (uint8_t i = 0; i < NO_OBJECTS; ++i) { 226 | if (strcmp(objects[i].imgFile, "") != 0) { 227 | sprintf(&path[0], "%s/%s", ICONS_PATH, objects[i].imgFile); 228 | loadSurface(&objects[i].img, path); 229 | if (objects[i].img == NULL) { 230 | sprintf(&path[0], "%s/%s", DEF_ICONS_PATH, objects[i].imgFile); 231 | fprintf(stderr,"Icon: %s not found. Loading default.\n", objects[i].imgFile); 232 | loadSurface(&objects[i].img, path); 233 | if (objects[i].img == NULL) { 234 | fprintf(stderr, "Default icon %s not found. Closing\n", objects[i].imgFile); 235 | exit(1); 236 | } 237 | } 238 | } 239 | } 240 | initButtonGuide(); 241 | } 242 | 243 | int loadJsonFile(char * path, char * defPath) { 244 | FILE *file; 245 | uint8_t retVal = 0; /* 0 - No file, 1 - Requested file, 2 - Default file */ 246 | const char *error_ptr; 247 | 248 | file = fopen(path, "r"); 249 | if (file == NULL) { 250 | fprintf(stderr, "File %s not found. Loading default file %s.\n", path, defPath); 251 | file = fopen(defPath, "r"); 252 | if (file == NULL) { 253 | fprintf(stderr, "Default file %s not found.\n", defPath); 254 | return 0; 255 | } 256 | retVal = 2; 257 | } else retVal = 1; 258 | fread(jsonFileBuffer, 1, sizeof(jsonFileBuffer), file); 259 | fclose(file); 260 | json = cJSON_Parse(jsonFileBuffer); 261 | if (json == NULL) { 262 | error_ptr = cJSON_GetErrorPtr(); 263 | if (error_ptr != NULL) fprintf(stderr, "Error parsing the JSON file: %s. Parsing the default file.\n", error_ptr); 264 | cJSON_Delete(json); 265 | if (retVal == 2) return 0; /* Both the requested and the deleted files failed */ 266 | file = fopen(defPath, "r"); 267 | if (file == NULL) { 268 | fprintf(stderr, "Default file %s not found.\n", defPath); 269 | return 0; 270 | } 271 | else retVal = 2; 272 | fread(jsonFileBuffer, 1, sizeof(jsonFileBuffer), file); 273 | json = cJSON_Parse(jsonFileBuffer); 274 | if (json == NULL) { 275 | error_ptr = cJSON_GetErrorPtr(); 276 | if (error_ptr != NULL) fprintf(stderr, "Error parsing the JSON file: %s. Parsing the default file.\n", error_ptr); 277 | cJSON_Delete(json); 278 | return 0; 279 | } 280 | return 1; 281 | } 282 | return retVal; 283 | } 284 | 285 | int loadCoreMapping(void) { 286 | if (loadJsonFile(COREMAPPING_JSON_PATH, DEF_COREMAPPING_JSON_PATH) == 0) { 287 | fprintf(stderr, "The ROM can not be started.\n"); 288 | return 0; 289 | } 290 | if (romsFound != 0) { 291 | cJSON *name = cJSON_GetObjectItemCaseSensitive(json, romsList[selRomIdx].system); 292 | if ((cJSON_IsString(name) != 0) && (name->valuestring != NULL)) strcpy(romCoreMapping, name->valuestring); 293 | else { 294 | cJSON_Delete(json); 295 | return 0; 296 | } 297 | } 298 | else { 299 | cJSON_Delete(json); 300 | return 0; 301 | } 302 | cJSON_Delete(json); 303 | return 1; 304 | } 305 | 306 | void loadSettings(void) { 307 | cJSON *name; 308 | 309 | if (loadJsonFile(SETTINGS_JSON_PATH, DEF_SETTINGS_JSON_PATH) == 0) { 310 | fprintf(stderr, "Default settings values loaded.\n"); 311 | strcpy(textAlign, DEF_TEXT_ALIGN); 312 | strcpy(keyboardAlign, DEF_KEYBOARD_ALIGN); 313 | textMargin = DEF_TEXT_MARGIN; 314 | activeColor = DEF_C_ACTIVE_TEXT; 315 | inactiveColor = DEF_C_INACTIVE_TEXT; 316 | guideColor = DEF_C_GUIDE_TEXT; 317 | return; 318 | } 319 | name = cJSON_GetObjectItemCaseSensitive(json, "text-alignment"); 320 | if (cJSON_IsString(name) != 0) { 321 | strcpy(textAlign, name->valuestring); 322 | if (strcmp(textAlign, "right") == 0) strcpy(keyboardAlign, "right"); 323 | else strcpy(keyboardAlign, "left"); 324 | } 325 | else { 326 | fprintf(stderr,"\"text-alignment\" not found in the json file.\n"); 327 | strcpy(textAlign, DEF_TEXT_ALIGN); 328 | strcpy(keyboardAlign, DEF_KEYBOARD_ALIGN); 329 | } 330 | name = cJSON_GetObjectItemCaseSensitive(json, "text-margin"); 331 | if (cJSON_IsNumber(name) != 0) textMargin = (int)name->valuedouble; 332 | else { 333 | fprintf(stderr,"\"text-margin\" not found in the json file.\n"); 334 | textMargin = DEF_TEXT_MARGIN; 335 | } 336 | guideColor = getColorFromJson("color-guide", DEF_C_GUIDE_TEXT); 337 | inactiveColor = getColorFromJson("color-inactive", DEF_C_INACTIVE_TEXT); 338 | activeColor = getColorFromJson("color-active", DEF_C_ACTIVE_TEXT); 339 | cJSON_Delete(json); 340 | } 341 | 342 | void loadSkin() { 343 | cJSON *name; 344 | loadJsonFile(SKIN_PATH, SKIN_PATH); 345 | skin.background = getColorFromJson("color-background", KB_C_BACKGND); 346 | skin.sbFrame = getColorFromJson("color-searchbox-frame",KB_C_SB_FRAME); 347 | skin.sb = getColorFromJson("color-searchbox", KB_C_SB); 348 | skin.st = getColorFromJson("color-search-text", KB_C_ST); 349 | skin.key = getColorFromJson("color-key", KB_C_KEY_TXT_SEL); 350 | skin.keySelTxt = getColorFromJson("color-selected-key-text", KB_C_KEY_TXT_SEL); 351 | skin.keyTxt = getColorFromJson("color-unselected-key-text", KB_C_KEY_TXT); 352 | skin.info = getColorFromJson("color-text-info", KB_C_INFO); 353 | skin.cursor = getColorFromJson("color-cursor", KB_C_CURSOR); 354 | name = cJSON_GetObjectItemCaseSensitive(json, "keyboard-and-result-pos-y"); 355 | if (cJSON_IsNumber(name) != 0) kbPosY = (int)name->valuedouble; 356 | else kbPosY = KB_Y; 357 | name = cJSON_GetObjectItemCaseSensitive(json, "system-pos-x"); 358 | if (cJSON_IsNumber(name) != 0) objects[OBJ_CONSOLE_INDEX].imgPos.x = (int)name->valuedouble; 359 | name = cJSON_GetObjectItemCaseSensitive(json, "system-pos-y"); 360 | if (cJSON_IsNumber(name) != 0) objects[OBJ_CONSOLE_INDEX].imgPos.y = (int)name->valuedouble; 361 | name = cJSON_GetObjectItemCaseSensitive(json, "hide-helper-buttons"); 362 | if (cJSON_IsNumber(name) != 0) hideHelper = (int)name->valuedouble; 363 | cJSON_Delete(json); 364 | } 365 | 366 | void loadLanguage() { 367 | cJSON *name; 368 | FILE *file; 369 | char * line = NULL; 370 | size_t len = 0; 371 | uint8_t jsonRes; 372 | 373 | sprintf(font, "%s/", DEF_FONT_PATH); 374 | file = fopen(LANGUAGE_FLAG_PATH, "r"); 375 | if (file != NULL) { 376 | if (getline(&line, &len, file) != -1) { 377 | sprintf(languagePath, "%s/%s", LANGUAGES_PATH,line); 378 | languagePath[strcspn(languagePath, "\r\n")] = '\0'; 379 | strcat(languagePath, ".json"); 380 | } 381 | else fprintf(stderr, "Language flag file is empty.\n"); 382 | fclose(file); 383 | } 384 | else fprintf(stderr, "Language flag file not found.\n"); 385 | romsFontSize = DEF_ROMS_FONT_SIZE; 386 | textFontSize = DEF_TEXT_FONT_SIZE; 387 | 388 | jsonRes = loadJsonFile(languagePath, DEF_LANGUAGES_PATH); 389 | if (jsonRes == 0) { 390 | strcpy(font, DEF_FONT); 391 | return; 392 | } 393 | else if (jsonRes == 1) sprintf(font, "%s/", FONTS_PATH); 394 | else sprintf(font, "%s/", DEF_FONT_PATH); 395 | name = cJSON_GetObjectItemCaseSensitive(json, "font"); 396 | if (cJSON_IsString(name) != 0) strcat(font, name->valuestring); 397 | else { 398 | fprintf(stderr,"\"font\" not found in the json file.\n"); 399 | strcpy(font, DEF_FONT); 400 | } 401 | name = cJSON_GetObjectItemCaseSensitive(json, "font-size"); 402 | if (cJSON_IsNumber(name) != 0) romsFontSize = (int)name->valuedouble; 403 | else fprintf(stderr,"\"font-size\" not found in the json file. Default set to %d\n", DEF_ROMS_FONT_SIZE); 404 | name = cJSON_GetObjectItemCaseSensitive(json, "button-guide-font-size"); 405 | if (cJSON_IsNumber(name) != 0) textFontSize = (int)name->valuedouble; 406 | else fprintf(stderr,"\"button-guide-font-size\" not found in the json file. Default set to %d\n", DEF_TEXT_FONT_SIZE); 407 | cJSON_Delete(json); 408 | } 409 | 410 | void searchRoms(char * romPath , uint8_t romLocation) { 411 | DIR * dir; 412 | DIR * console; 413 | char * ptrDispName; 414 | struct dirent * entry; 415 | struct dirent * rom; 416 | char path[PATH_MAX + 1]; 417 | if ((dir = opendir (romPath)) == NULL) return; 418 | while ((entry = readdir (dir)) != NULL) { 419 | if ((entry->d_type == DT_DIR) && (strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0)) { 420 | sprintf(&path[0], "%s/%s", romPath, entry->d_name); 421 | if ((console = opendir (path)) == NULL) return; 422 | while ((rom = readdir(console)) != NULL) { 423 | if ((rom->d_type == DT_REG) && (romsFound < NUMBER_OF_FILES)) { 424 | strcpy(romsList[romsFound].displayName, rom->d_name); 425 | ptrDispName = strrchr (romsList[romsFound].displayName, '.'); 426 | if (ptrDispName != NULL) ptrDispName[0] = '\0'; 427 | for (uint8_t i = 0; i < MAME_CONSOLE_COUNT; ++i) 428 | if (strcmp(entry->d_name, &mamelist[i][0]) == 0) { 429 | searchMameRomFast(romsList[romsFound].displayName); 430 | break; 431 | } 432 | if ((strcasestr(romsList[romsFound].displayName, searchWord)) != NULL) { 433 | romsList[romsFound].romLocation = romLocation; 434 | strcpy(romsList[romsFound].system, entry->d_name); 435 | strcpy(romsList[romsFound].fileName, rom->d_name); 436 | romsFound++; 437 | } 438 | } 439 | } 440 | closedir(console); 441 | } 442 | } 443 | closedir(dir); 444 | } 445 | 446 | void searchFillRomList() { 447 | romsFound = 0; 448 | searchRoms(SD1_ROMS_PATH, 0); 449 | searchRoms(SD2_ROMS_PATH, 1); 450 | } 451 | 452 | void bufferMameFile() { 453 | FILE * mameFile; 454 | char * line; 455 | size_t len = 0; 456 | 457 | mameFile = fopen(MAME_FILELIST_PATH, "r"); 458 | if (mameFile == NULL) { 459 | fprintf(stderr, "mame.csv not found. Loading default mame.csv.\n"); 460 | mameFile = fopen(DEF_MAME_FILELIST_PATH, "r"); 461 | if (mameFile == NULL) fprintf(stderr, "Default mame.csv not found.\n"); 462 | } 463 | while (getline(&line, &len, mameFile) != -1) { 464 | sscanf(line, "%[^,],%[^,]", &mameFileBufferTag[mameFileSize], &mameFileBufferLong[mameFileSize]); 465 | mameFileSize++; 466 | } 467 | fclose(mameFile); 468 | } 469 | 470 | int setup() { 471 | FILE *file = fopen(ROMSEARCH_SH_PATH, "r"); 472 | 473 | if (file != NULL) { 474 | fprintf(stderr, "romSearch.sh found! \n"); 475 | fclose(file); 476 | return 0; 477 | } 478 | bufferMameFile(); 479 | if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { 480 | fprintf(stderr, "SDL Init error...\n"); 481 | return 0; 482 | } 483 | if (TTF_Init() != 0) { 484 | fprintf(stderr, "TTF Init error...\n"); 485 | return 0; 486 | } 487 | if (IMG_Init(IMG_INIT_PNG) == 0) { 488 | fprintf(stderr, "IMG Init error...\n"); 489 | return 0; 490 | } 491 | SDL_ShowCursor(SDL_DISABLE); 492 | screen = SDL_SetVideoMode(WINDOW_W, WINDOW_H, 32, SDL_SWSURFACE); 493 | loadSkin(); 494 | loadLanguage(); 495 | textFont = TTF_OpenFont(font, textFontSize); 496 | if (textFont == NULL) { 497 | fprintf(stderr, "Font not found. Loading default font.\n"); 498 | textFont = TTF_OpenFont(DEF_FONT, textFontSize); 499 | if (textFont == NULL) { 500 | fprintf(stderr, "Default font not found!. Closing.\n"); 501 | return 0; 502 | } 503 | } 504 | romsFont = TTF_OpenFont(font, romsFontSize); 505 | if (romsFont == NULL) { 506 | fprintf(stderr, "Font not found. Loading default font.\n"); 507 | romsFont = TTF_OpenFont(DEF_FONT, romsFontSize); 508 | if (romsFont == NULL) { 509 | fprintf(stderr, "Default font not found!. Closing.\n"); 510 | return 0; 511 | } 512 | } 513 | keyboardFont = TTF_OpenFont(font, KEYBOARD_FONT_SIZE); 514 | if (keyboardFont == NULL) { 515 | fprintf(stderr, "Font not found. Loading default font.\n"); 516 | keyboardFont = TTF_OpenFont(DEF_FONT, KEYBOARD_FONT_SIZE); 517 | if (keyboardFont == NULL) { 518 | fprintf(stderr, "Default font not found!. Closing.\n"); 519 | return 0; 520 | } 521 | } 522 | searchWordFont = TTF_OpenFont(font, SEARCH_WORD_FONT_SIZE); 523 | if (searchWordFont == NULL) { 524 | fprintf(stderr, "Font not found. Loading default font.\n"); 525 | searchWordFont = TTF_OpenFont(DEF_FONT, SEARCH_WORD_FONT_SIZE); 526 | if (textFont == NULL) { 527 | fprintf(stderr, "Default font not found!. Closing.\n"); 528 | return 0; 529 | } 530 | } 531 | loadSurfaceFullScreen(&background, BACKGROUND_PATH); 532 | if (background == NULL) { 533 | fprintf(stderr, "Background not found. Loading default background.\n"); 534 | loadSurfaceFullScreen(&background, DEF_BACKGROUND_PATH); 535 | if (background == NULL) 536 | fprintf(stderr, "Default background not found.\n"); 537 | } 538 | loadSettings(); 539 | initObjects(); 540 | return 1; 541 | } 542 | 543 | void createCommandScript() { 544 | if (loadCoreMapping() == 0) return; 545 | FILE *file = fopen(ROMSEARCH_SH_PATH, "w"); 546 | if (file == NULL) { 547 | fprintf(stderr, "Cannot open romsearch.sh file.\n"); 548 | return; 549 | } 550 | fprintf(file, "#!/bin/sh\n"); 551 | fprintf(file, "export LANG=en_us\n"); 552 | if (romsList[selRomIdx].romLocation == 0) 553 | fprintf(file, "/mnt/mmc/CFW/retroarch/retroarch -L \"/mnt/mmc/CFW/retroarch/.retroarch/cores/%s\" \"%s/%s/%s\"\n", 554 | romCoreMapping, SD1_ROMS_PATH, romsList[selRomIdx].system, romsList[selRomIdx].fileName); 555 | else 556 | fprintf(file, "/mnt/mmc/CFW/retroarch/retroarch -L \"/mnt/mmc/CFW/retroarch/.retroarch/cores/%s\" \"%s/%s/%s\"\n", 557 | romCoreMapping, SD2_ROMS_PATH, romsList[selRomIdx].system, romsList[selRomIdx].fileName); 558 | fprintf(file, "exit $?\n"); 559 | fclose(file); 560 | } 561 | 562 | void processInput() { 563 | uint32_t delta; 564 | SDL_PollEvent(&event); 565 | int fw, fh; 566 | switch(event.type) { 567 | case SDL_QUIT: 568 | running = 0; 569 | break; 570 | case SDL_KEYDOWN: 571 | delta = SDL_GetTicks() - keyDownTime; 572 | if (delta > pressDelay) { 573 | if ((keyDownTime != 0) && (delta > FIRST_PRESS_DELAY)) pressDelay = PRESS_DELAY; 574 | keyDownTime = SDL_GetTicks(); 575 | switch(event.key.keysym.sym) { 576 | case MENU_BUTTON: 577 | running = 0; 578 | break; 579 | case RG35_DOWN_CODE: 580 | if (keyboardShown == 0) 581 | if ((romsFound != 0) && (selRomIdx < romsFound - 1)) selRomIdx++; 582 | else selRomIdx = 0; 583 | else if (selKeyIIdx < 3) selKeyIIdx++; 584 | else selKeyIIdx = 0; 585 | break; 586 | case RG35_UP_CODE: 587 | if (keyboardShown == 0) 588 | if (selRomIdx > 0) selRomIdx--; 589 | else selRomIdx = romsFound - 1; 590 | else if (selKeyIIdx > 0) selKeyIIdx--; 591 | else selKeyIIdx = 3; 592 | break; 593 | case RG35_RIGHT_CODE: 594 | if (keyboardShown != 0) 595 | if (selKeyJIdx < 9) selKeyJIdx++; 596 | else selKeyJIdx = 0; 597 | break; 598 | case RG35_LEFT_CODE: 599 | if (keyboardShown != 0) 600 | if (selKeyJIdx > 0) selKeyJIdx--; 601 | else selKeyJIdx = 9; 602 | break; 603 | case RG35_A_CODE: 604 | if (keyboardShown != 0) { 605 | TTF_SizeUTF8(searchWordFont, searchWord, &fw, &fh); 606 | if (fw + KB_X + 16 < SB_W) { 607 | strcat(searchWord, keyboardLayout[selKeyIIdx][selKeyJIdx]); 608 | startSearch = 1; 609 | } 610 | updateScreenshot = 1; 611 | } else { 612 | createCommandScript(); 613 | running = 0; 614 | } 615 | break; 616 | case RG35_B_CODE: 617 | if (keyboardShown != 0) { 618 | searchWord[strlen(searchWord)-1] = '\0'; 619 | if (strlen(searchWord) > 0) 620 | startSearch = 1; 621 | else { 622 | romsFound = 0; 623 | startSearch = 0; 624 | } 625 | updateScreenshot = 1; 626 | } 627 | else running = 0; 628 | break; 629 | case RG35_X_CODE: 630 | if (keyboardShown != 0) { 631 | if (strcmp(keyboardAlign, "left") == 0) strcpy(keyboardAlign, "right"); 632 | else strcpy(keyboardAlign,"left"); 633 | } 634 | break; 635 | case RG35_Y_CODE: 636 | keyboardShown = 1 - keyboardShown; 637 | break; 638 | case RG35_SELECT_CODE: 639 | if ((keyboardShown != 0) && (strlen(searchWord) < SEARCH_WORD_SIZE)) 640 | strcat(searchWord, " "); 641 | break; 642 | case RG35_START_CODE: 643 | if ((keyboardShown != 0) && (strlen(searchWord) != 0)) keyboardShown = 0; 644 | break; 645 | default: 646 | break; 647 | } 648 | } 649 | break; 650 | case SDL_KEYUP: 651 | keyDownTime = 0; 652 | pressDelay = FIRST_PRESS_DELAY; 653 | break; 654 | default: 655 | break; 656 | } 657 | } 658 | 659 | 660 | void loadScreenshot(void) { 661 | char path[PATH_MAX + 1] = ""; 662 | char * extension; 663 | 664 | if (romsFound != 0) { 665 | objects[OBJ_SCREENSHOT_INDEX].imgVisible = 1; 666 | objects[OBJ_CONSOLE_INDEX].imgVisible = 1; 667 | if (romsList[selRomIdx].romLocation == 0) { 668 | sprintf(path, "%s/%s/Imgs/%s", SD1_ROMS_PATH, romsList[selRomIdx].system, romsList[selRomIdx].fileName); 669 | strcpy(objects[0].name, "SEARCH (SD1)"); 670 | } else { 671 | sprintf(path, "%s/%s/Imgs/%s", SD2_ROMS_PATH, romsList[selRomIdx].system, romsList[selRomIdx].fileName); 672 | strcpy(objects[0].name, "SEARCH (SD2)"); 673 | } 674 | if ((extension = strrchr(path, '.')) != NULL) extension[0] = '\0'; 675 | strcat(path,".png"); 676 | freeSurface(&objects[OBJ_SCREENSHOT_INDEX].img); 677 | if (loadSurfaceFullScreen(&objects[OBJ_SCREENSHOT_INDEX].img, path) == 0) 678 | objects[OBJ_SCREENSHOT_INDEX].imgVisible = 0; 679 | sprintf(path, "%s/system/%s.png", ICONS_PATH, romsList[selRomIdx].system); 680 | freeSurface(&objects[OBJ_CONSOLE_INDEX].img); 681 | if (loadSurface(&objects[OBJ_CONSOLE_INDEX].img, path) == 0) 682 | objects[OBJ_CONSOLE_INDEX].imgVisible = 0; 683 | } else { 684 | objects[OBJ_SCREENSHOT_INDEX].imgVisible = 0; 685 | objects[OBJ_CONSOLE_INDEX].imgVisible = 0; 686 | strcpy(objects[0].name, "SEARCH"); 687 | } 688 | } 689 | 690 | void update(int delta) { 691 | if (startSearch != 0) { 692 | startSearch = 0; 693 | searchFillRomList(); 694 | } 695 | if ((selRomIdx != selRomIdxOld) || (updateScreenshot != 0)) { 696 | selRomIdxOld = selRomIdx; 697 | updateScreenshot = 0; 698 | loadScreenshot(); 699 | } 700 | } 701 | 702 | void drawObjects(void) { 703 | for (uint8_t i; i < NO_OBJECTS; ++i) { 704 | if (objects[i].imgVisible == 1) SDL_BlitSurface(objects[i].img, NULL, screen, &objects[i].imgPos); 705 | if (objects[i].textVisible == 1) drawText(objects[i].textPos, 0, objects[i].name, textFont, guideColor, textAlign, 1); 706 | } 707 | } 708 | 709 | void drawRomsTextbox(void) { 710 | uint8_t first; 711 | SDL_Rect pos; 712 | int o, fw, fh; 713 | 714 | selRomIdx = (selRomIdx >= romsFound) ? romsFound - 1 : selRomIdx; 715 | first = (selRomIdx < 4) ? 0 : selRomIdx - 3; 716 | for (uint8_t i = 0; (i < 8) && ((i + first)< romsFound); ++i) { 717 | TTF_SizeUTF8(romsFont, romsList[i+first].displayName, &fw, &fh); 718 | pos = rect(textMargin, kbPosY + (KB_H - (8 * fh)) / 2 + i * fh, 0, 0); 719 | o = WINDOW_W - fw - pos.x - textMargin; 720 | if ((i + first) == selRomIdx) drawText(pos, o, romsList[i + first].displayName, romsFont, activeColor, textAlign, 2); 721 | else drawText(pos, o, romsList[i + first].displayName, romsFont, inactiveColor, textAlign, 3); 722 | } 723 | } 724 | 725 | void drawKeyboard() { 726 | int delta, o = KB_FLIP_OFFSET; 727 | int fw, fh; 728 | int kby = kbPosY; 729 | 730 | TTF_SizeUTF8(searchWordFont, searchWord, &fw, &fh); 731 | drawRect(rect(KB_X, kby, KB_W, KB_H), o, skin.background, keyboardAlign); 732 | drawRect(rect(KB_X + 6, kby + 10, SB_W, SB_H), o, skin.sbFrame, keyboardAlign); 733 | drawRect(rect(KB_X + 8, kby + 12, SB_W - 4, SB_H - 4), o, skin.sb, keyboardAlign); 734 | drawText(rect(KB_X + 16, kby + 20, 0, 0), o, searchWord, searchWordFont, skin.st, keyboardAlign, 4); 735 | delta = SDL_GetTicks() - cursorOld; 736 | if (delta > (2 * CURSOR_BLINK_DELAY)) { 737 | drawRect(rect(KB_X + 17 + fw, kby + 22, 2, 22), o, skin.sb, keyboardAlign); 738 | cursorOld = SDL_GetTicks(); 739 | } 740 | else if (delta > CURSOR_BLINK_DELAY) drawRect(rect(KB_X + 17 + fw, kby + 22, 2, 22), o, skin.cursor, keyboardAlign); 741 | for (uint8_t i = 0; i < KB_ROWS; ++i) { 742 | for (uint8_t j = 0; j < KB_COLUMNS; ++j) { 743 | if ((i == selKeyIIdx) && (j == selKeyJIdx)) { 744 | drawRect(rect(KB_X + 5 + (KY_W + 2) * j, kby + 67 + (KY_H + 2) * i, KY_W + 2, KY_H + 2), o, skin.keyTxt, keyboardAlign); 745 | drawKey(KB_X + 6 + (KY_W + 2) * j, kby + 68 + (KY_H + 2) * i, o, keyboardLayout[i][j], skin.keySelTxt); 746 | } else drawKey(KB_X + 6 + (KY_W + 2) * j, kby + 68 + (KY_H + 2) * i, o, keyboardLayout[i][j], skin.keyTxt); 747 | } 748 | drawText(rect(KB_X + 8, kby + 240, 0, 0), o, "Select/Space", keyboardFont, skin.info, keyboardAlign, 5); 749 | TTF_SizeUTF8(keyboardFont, "Start/Done", &fw, &fh); 750 | drawText(rect(KB_W - fw, kby + 240, 0, 0), o, "Start/Done", keyboardFont, skin.info, keyboardAlign, 6); 751 | } 752 | } 753 | 754 | void render(void) { 755 | Uint32 mapColor = SDL_MapRGB(screen->format,BACKGROUND_C.r, BACKGROUND_C.g, BACKGROUND_C.b); 756 | SDL_FillRect(screen, NULL, mapColor); 757 | SDL_BlitSurface(background, NULL, screen, &backgroundLocation); 758 | drawObjects(); 759 | if (romsFound != 0) drawRomsTextbox(); 760 | if (keyboardShown != 0) drawKeyboard(); 761 | SDL_Flip(screen); 762 | } 763 | 764 | void cleanup() { 765 | for (int i = 0; i < NO_OBJECTS; ++i) { 766 | freeSurface(&objects[i].img); 767 | } 768 | freeSurface(&screen); 769 | freeSurface(&background); 770 | TTF_CloseFont(textFont); 771 | TTF_CloseFont(romsFont); 772 | TTF_CloseFont(keyboardFont); 773 | TTF_Quit(); 774 | IMG_Quit(); 775 | SDL_Quit(); 776 | } 777 | 778 | int main() { 779 | uint32_t new = 0; 780 | uint32_t old = 0; 781 | uint32_t delta = 0; 782 | 783 | if (setup() == 0) return 0; 784 | while (running == 1) { 785 | new = SDL_GetTicks(); 786 | delta = new - old; /* Time since last frame */ 787 | processInput(); 788 | if (delta >= FRAME_TIME) { 789 | update(delta); 790 | render(); 791 | old = new; 792 | } 793 | else SDL_Delay(FRAME_TIME - delta); 794 | } 795 | cleanup(); 796 | return 0; 797 | } 798 | -------------------------------------------------------------------------------- /src/cJSON.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* cJSON */ 24 | /* JSON parser in C. */ 25 | 26 | /* disable warnings about old C89 functions in MSVC */ 27 | #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) 28 | #define _CRT_SECURE_NO_DEPRECATE 29 | #endif 30 | 31 | #ifdef __GNUC__ 32 | #pragma GCC visibility push(default) 33 | #endif 34 | #if defined(_MSC_VER) 35 | #pragma warning (push) 36 | /* disable warning about single line comments in system headers */ 37 | #pragma warning (disable : 4001) 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #ifdef ENABLE_LOCALES 49 | #include 50 | #endif 51 | 52 | #if defined(_MSC_VER) 53 | #pragma warning (pop) 54 | #endif 55 | #ifdef __GNUC__ 56 | #pragma GCC visibility pop 57 | #endif 58 | 59 | #include "cJSON.h" 60 | 61 | /* define our own boolean type */ 62 | #ifdef true 63 | #undef true 64 | #endif 65 | #define true ((cJSON_bool)1) 66 | 67 | #ifdef false 68 | #undef false 69 | #endif 70 | #define false ((cJSON_bool)0) 71 | 72 | /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ 73 | #ifndef isinf 74 | #define isinf(d) (isnan((d - d)) && !isnan(d)) 75 | #endif 76 | #ifndef isnan 77 | #define isnan(d) (d != d) 78 | #endif 79 | 80 | #ifndef NAN 81 | #ifdef _WIN32 82 | #define NAN sqrt(-1.0) 83 | #else 84 | #define NAN 0.0/0.0 85 | #endif 86 | #endif 87 | 88 | typedef struct { 89 | const unsigned char *json; 90 | size_t position; 91 | } error; 92 | static error global_error = { NULL, 0 }; 93 | 94 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) 95 | { 96 | return (const char*) (global_error.json + global_error.position); 97 | } 98 | 99 | CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) 100 | { 101 | if (!cJSON_IsString(item)) 102 | { 103 | return NULL; 104 | } 105 | 106 | return item->valuestring; 107 | } 108 | 109 | CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) 110 | { 111 | if (!cJSON_IsNumber(item)) 112 | { 113 | return (double) NAN; 114 | } 115 | 116 | return item->valuedouble; 117 | } 118 | 119 | /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ 120 | #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) 121 | #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. 122 | #endif 123 | 124 | CJSON_PUBLIC(const char*) cJSON_Version(void) 125 | { 126 | static char version[15]; 127 | sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); 128 | 129 | return version; 130 | } 131 | 132 | /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ 133 | static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) 134 | { 135 | if ((string1 == NULL) || (string2 == NULL)) 136 | { 137 | return 1; 138 | } 139 | 140 | if (string1 == string2) 141 | { 142 | return 0; 143 | } 144 | 145 | for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) 146 | { 147 | if (*string1 == '\0') 148 | { 149 | return 0; 150 | } 151 | } 152 | 153 | return tolower(*string1) - tolower(*string2); 154 | } 155 | 156 | typedef struct internal_hooks 157 | { 158 | void *(CJSON_CDECL *allocate)(size_t size); 159 | void (CJSON_CDECL *deallocate)(void *pointer); 160 | void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); 161 | } internal_hooks; 162 | 163 | #if defined(_MSC_VER) 164 | /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ 165 | static void * CJSON_CDECL internal_malloc(size_t size) 166 | { 167 | return malloc(size); 168 | } 169 | static void CJSON_CDECL internal_free(void *pointer) 170 | { 171 | free(pointer); 172 | } 173 | static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) 174 | { 175 | return realloc(pointer, size); 176 | } 177 | #else 178 | #define internal_malloc malloc 179 | #define internal_free free 180 | #define internal_realloc realloc 181 | #endif 182 | 183 | /* strlen of character literals resolved at compile time */ 184 | #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) 185 | 186 | static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; 187 | 188 | static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) 189 | { 190 | size_t length = 0; 191 | unsigned char *copy = NULL; 192 | 193 | if (string == NULL) 194 | { 195 | return NULL; 196 | } 197 | 198 | length = strlen((const char*)string) + sizeof(""); 199 | copy = (unsigned char*)hooks->allocate(length); 200 | if (copy == NULL) 201 | { 202 | return NULL; 203 | } 204 | memcpy(copy, string, length); 205 | 206 | return copy; 207 | } 208 | 209 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) 210 | { 211 | if (hooks == NULL) 212 | { 213 | /* Reset hooks */ 214 | global_hooks.allocate = malloc; 215 | global_hooks.deallocate = free; 216 | global_hooks.reallocate = realloc; 217 | return; 218 | } 219 | 220 | global_hooks.allocate = malloc; 221 | if (hooks->malloc_fn != NULL) 222 | { 223 | global_hooks.allocate = hooks->malloc_fn; 224 | } 225 | 226 | global_hooks.deallocate = free; 227 | if (hooks->free_fn != NULL) 228 | { 229 | global_hooks.deallocate = hooks->free_fn; 230 | } 231 | 232 | /* use realloc only if both free and malloc are used */ 233 | global_hooks.reallocate = NULL; 234 | if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) 235 | { 236 | global_hooks.reallocate = realloc; 237 | } 238 | } 239 | 240 | /* Internal constructor. */ 241 | static cJSON *cJSON_New_Item(const internal_hooks * const hooks) 242 | { 243 | cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); 244 | if (node) 245 | { 246 | memset(node, '\0', sizeof(cJSON)); 247 | } 248 | 249 | return node; 250 | } 251 | 252 | /* Delete a cJSON structure. */ 253 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) 254 | { 255 | cJSON *next = NULL; 256 | while (item != NULL) 257 | { 258 | next = item->next; 259 | if (!(item->type & cJSON_IsReference) && (item->child != NULL)) 260 | { 261 | cJSON_Delete(item->child); 262 | } 263 | if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) 264 | { 265 | global_hooks.deallocate(item->valuestring); 266 | } 267 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) 268 | { 269 | global_hooks.deallocate(item->string); 270 | } 271 | global_hooks.deallocate(item); 272 | item = next; 273 | } 274 | } 275 | 276 | /* get the decimal point character of the current locale */ 277 | static unsigned char get_decimal_point(void) 278 | { 279 | #ifdef ENABLE_LOCALES 280 | struct lconv *lconv = localeconv(); 281 | return (unsigned char) lconv->decimal_point[0]; 282 | #else 283 | return '.'; 284 | #endif 285 | } 286 | 287 | typedef struct 288 | { 289 | const unsigned char *content; 290 | size_t length; 291 | size_t offset; 292 | size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ 293 | internal_hooks hooks; 294 | } parse_buffer; 295 | 296 | /* check if the given size is left to read in a given parse buffer (starting with 1) */ 297 | #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) 298 | /* check if the buffer can be accessed at the given index (starting with 0) */ 299 | #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) 300 | #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) 301 | /* get a pointer to the buffer at the position */ 302 | #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) 303 | 304 | /* Parse the input text to generate a number, and populate the result into item. */ 305 | static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) 306 | { 307 | double number = 0; 308 | unsigned char *after_end = NULL; 309 | unsigned char number_c_string[64]; 310 | unsigned char decimal_point = get_decimal_point(); 311 | size_t i = 0; 312 | 313 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) 314 | { 315 | return false; 316 | } 317 | 318 | /* copy the number into a temporary buffer and replace '.' with the decimal point 319 | * of the current locale (for strtod) 320 | * This also takes care of '\0' not necessarily being available for marking the end of the input */ 321 | for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) 322 | { 323 | switch (buffer_at_offset(input_buffer)[i]) 324 | { 325 | case '0': 326 | case '1': 327 | case '2': 328 | case '3': 329 | case '4': 330 | case '5': 331 | case '6': 332 | case '7': 333 | case '8': 334 | case '9': 335 | case '+': 336 | case '-': 337 | case 'e': 338 | case 'E': 339 | number_c_string[i] = buffer_at_offset(input_buffer)[i]; 340 | break; 341 | 342 | case '.': 343 | number_c_string[i] = decimal_point; 344 | break; 345 | 346 | default: 347 | goto loop_end; 348 | } 349 | } 350 | loop_end: 351 | number_c_string[i] = '\0'; 352 | 353 | number = strtod((const char*)number_c_string, (char**)&after_end); 354 | if (number_c_string == after_end) 355 | { 356 | return false; /* parse_error */ 357 | } 358 | 359 | item->valuedouble = number; 360 | 361 | /* use saturation in case of overflow */ 362 | if (number >= INT_MAX) 363 | { 364 | item->valueint = INT_MAX; 365 | } 366 | else if (number <= (double)INT_MIN) 367 | { 368 | item->valueint = INT_MIN; 369 | } 370 | else 371 | { 372 | item->valueint = (int)number; 373 | } 374 | 375 | item->type = cJSON_Number; 376 | 377 | input_buffer->offset += (size_t)(after_end - number_c_string); 378 | return true; 379 | } 380 | 381 | /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ 382 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) 383 | { 384 | if (number >= INT_MAX) 385 | { 386 | object->valueint = INT_MAX; 387 | } 388 | else if (number <= (double)INT_MIN) 389 | { 390 | object->valueint = INT_MIN; 391 | } 392 | else 393 | { 394 | object->valueint = (int)number; 395 | } 396 | 397 | return object->valuedouble = number; 398 | } 399 | 400 | CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) 401 | { 402 | char *copy = NULL; 403 | /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ 404 | if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) 405 | { 406 | return NULL; 407 | } 408 | if (strlen(valuestring) <= strlen(object->valuestring)) 409 | { 410 | strcpy(object->valuestring, valuestring); 411 | return object->valuestring; 412 | } 413 | copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); 414 | if (copy == NULL) 415 | { 416 | return NULL; 417 | } 418 | if (object->valuestring != NULL) 419 | { 420 | cJSON_free(object->valuestring); 421 | } 422 | object->valuestring = copy; 423 | 424 | return copy; 425 | } 426 | 427 | typedef struct 428 | { 429 | unsigned char *buffer; 430 | size_t length; 431 | size_t offset; 432 | size_t depth; /* current nesting depth (for formatted printing) */ 433 | cJSON_bool noalloc; 434 | cJSON_bool format; /* is this print a formatted print */ 435 | internal_hooks hooks; 436 | } printbuffer; 437 | 438 | /* realloc printbuffer if necessary to have at least "needed" bytes more */ 439 | static unsigned char* ensure(printbuffer * const p, size_t needed) 440 | { 441 | unsigned char *newbuffer = NULL; 442 | size_t newsize = 0; 443 | 444 | if ((p == NULL) || (p->buffer == NULL)) 445 | { 446 | return NULL; 447 | } 448 | 449 | if ((p->length > 0) && (p->offset >= p->length)) 450 | { 451 | /* make sure that offset is valid */ 452 | return NULL; 453 | } 454 | 455 | if (needed > INT_MAX) 456 | { 457 | /* sizes bigger than INT_MAX are currently not supported */ 458 | return NULL; 459 | } 460 | 461 | needed += p->offset + 1; 462 | if (needed <= p->length) 463 | { 464 | return p->buffer + p->offset; 465 | } 466 | 467 | if (p->noalloc) { 468 | return NULL; 469 | } 470 | 471 | /* calculate new buffer size */ 472 | if (needed > (INT_MAX / 2)) 473 | { 474 | /* overflow of int, use INT_MAX if possible */ 475 | if (needed <= INT_MAX) 476 | { 477 | newsize = INT_MAX; 478 | } 479 | else 480 | { 481 | return NULL; 482 | } 483 | } 484 | else 485 | { 486 | newsize = needed * 2; 487 | } 488 | 489 | if (p->hooks.reallocate != NULL) 490 | { 491 | /* reallocate with realloc if available */ 492 | newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); 493 | if (newbuffer == NULL) 494 | { 495 | p->hooks.deallocate(p->buffer); 496 | p->length = 0; 497 | p->buffer = NULL; 498 | 499 | return NULL; 500 | } 501 | } 502 | else 503 | { 504 | /* otherwise reallocate manually */ 505 | newbuffer = (unsigned char*)p->hooks.allocate(newsize); 506 | if (!newbuffer) 507 | { 508 | p->hooks.deallocate(p->buffer); 509 | p->length = 0; 510 | p->buffer = NULL; 511 | 512 | return NULL; 513 | } 514 | 515 | memcpy(newbuffer, p->buffer, p->offset + 1); 516 | p->hooks.deallocate(p->buffer); 517 | } 518 | p->length = newsize; 519 | p->buffer = newbuffer; 520 | 521 | return newbuffer + p->offset; 522 | } 523 | 524 | /* calculate the new length of the string in a printbuffer and update the offset */ 525 | static void update_offset(printbuffer * const buffer) 526 | { 527 | const unsigned char *buffer_pointer = NULL; 528 | if ((buffer == NULL) || (buffer->buffer == NULL)) 529 | { 530 | return; 531 | } 532 | buffer_pointer = buffer->buffer + buffer->offset; 533 | 534 | buffer->offset += strlen((const char*)buffer_pointer); 535 | } 536 | 537 | /* securely comparison of floating-point variables */ 538 | static cJSON_bool compare_double(double a, double b) 539 | { 540 | double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); 541 | return (fabs(a - b) <= maxVal * DBL_EPSILON); 542 | } 543 | 544 | /* Render the number nicely from the given item into a string. */ 545 | static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) 546 | { 547 | unsigned char *output_pointer = NULL; 548 | double d = item->valuedouble; 549 | int length = 0; 550 | size_t i = 0; 551 | unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ 552 | unsigned char decimal_point = get_decimal_point(); 553 | double test = 0.0; 554 | 555 | if (output_buffer == NULL) 556 | { 557 | return false; 558 | } 559 | 560 | /* This checks for NaN and Infinity */ 561 | if (isnan(d) || isinf(d)) 562 | { 563 | length = sprintf((char*)number_buffer, "null"); 564 | } 565 | else if(d == (double)item->valueint) 566 | { 567 | length = sprintf((char*)number_buffer, "%d", item->valueint); 568 | } 569 | else 570 | { 571 | /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ 572 | length = sprintf((char*)number_buffer, "%1.15g", d); 573 | 574 | /* Check whether the original double can be recovered */ 575 | if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) 576 | { 577 | /* If not, print with 17 decimal places of precision */ 578 | length = sprintf((char*)number_buffer, "%1.17g", d); 579 | } 580 | } 581 | 582 | /* sprintf failed or buffer overrun occurred */ 583 | if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) 584 | { 585 | return false; 586 | } 587 | 588 | /* reserve appropriate space in the output */ 589 | output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); 590 | if (output_pointer == NULL) 591 | { 592 | return false; 593 | } 594 | 595 | /* copy the printed number to the output and replace locale 596 | * dependent decimal point with '.' */ 597 | for (i = 0; i < ((size_t)length); i++) 598 | { 599 | if (number_buffer[i] == decimal_point) 600 | { 601 | output_pointer[i] = '.'; 602 | continue; 603 | } 604 | 605 | output_pointer[i] = number_buffer[i]; 606 | } 607 | output_pointer[i] = '\0'; 608 | 609 | output_buffer->offset += (size_t)length; 610 | 611 | return true; 612 | } 613 | 614 | /* parse 4 digit hexadecimal number */ 615 | static unsigned parse_hex4(const unsigned char * const input) 616 | { 617 | unsigned int h = 0; 618 | size_t i = 0; 619 | 620 | for (i = 0; i < 4; i++) 621 | { 622 | /* parse digit */ 623 | if ((input[i] >= '0') && (input[i] <= '9')) 624 | { 625 | h += (unsigned int) input[i] - '0'; 626 | } 627 | else if ((input[i] >= 'A') && (input[i] <= 'F')) 628 | { 629 | h += (unsigned int) 10 + input[i] - 'A'; 630 | } 631 | else if ((input[i] >= 'a') && (input[i] <= 'f')) 632 | { 633 | h += (unsigned int) 10 + input[i] - 'a'; 634 | } 635 | else /* invalid */ 636 | { 637 | return 0; 638 | } 639 | 640 | if (i < 3) 641 | { 642 | /* shift left to make place for the next nibble */ 643 | h = h << 4; 644 | } 645 | } 646 | 647 | return h; 648 | } 649 | 650 | /* converts a UTF-16 literal to UTF-8 651 | * A literal can be one or two sequences of the form \uXXXX */ 652 | static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) 653 | { 654 | long unsigned int codepoint = 0; 655 | unsigned int first_code = 0; 656 | const unsigned char *first_sequence = input_pointer; 657 | unsigned char utf8_length = 0; 658 | unsigned char utf8_position = 0; 659 | unsigned char sequence_length = 0; 660 | unsigned char first_byte_mark = 0; 661 | 662 | if ((input_end - first_sequence) < 6) 663 | { 664 | /* input ends unexpectedly */ 665 | goto fail; 666 | } 667 | 668 | /* get the first utf16 sequence */ 669 | first_code = parse_hex4(first_sequence + 2); 670 | 671 | /* check that the code is valid */ 672 | if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) 673 | { 674 | goto fail; 675 | } 676 | 677 | /* UTF16 surrogate pair */ 678 | if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) 679 | { 680 | const unsigned char *second_sequence = first_sequence + 6; 681 | unsigned int second_code = 0; 682 | sequence_length = 12; /* \uXXXX\uXXXX */ 683 | 684 | if ((input_end - second_sequence) < 6) 685 | { 686 | /* input ends unexpectedly */ 687 | goto fail; 688 | } 689 | 690 | if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) 691 | { 692 | /* missing second half of the surrogate pair */ 693 | goto fail; 694 | } 695 | 696 | /* get the second utf16 sequence */ 697 | second_code = parse_hex4(second_sequence + 2); 698 | /* check that the code is valid */ 699 | if ((second_code < 0xDC00) || (second_code > 0xDFFF)) 700 | { 701 | /* invalid second half of the surrogate pair */ 702 | goto fail; 703 | } 704 | 705 | 706 | /* calculate the unicode codepoint from the surrogate pair */ 707 | codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); 708 | } 709 | else 710 | { 711 | sequence_length = 6; /* \uXXXX */ 712 | codepoint = first_code; 713 | } 714 | 715 | /* encode as UTF-8 716 | * takes at maximum 4 bytes to encode: 717 | * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 718 | if (codepoint < 0x80) 719 | { 720 | /* normal ascii, encoding 0xxxxxxx */ 721 | utf8_length = 1; 722 | } 723 | else if (codepoint < 0x800) 724 | { 725 | /* two bytes, encoding 110xxxxx 10xxxxxx */ 726 | utf8_length = 2; 727 | first_byte_mark = 0xC0; /* 11000000 */ 728 | } 729 | else if (codepoint < 0x10000) 730 | { 731 | /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ 732 | utf8_length = 3; 733 | first_byte_mark = 0xE0; /* 11100000 */ 734 | } 735 | else if (codepoint <= 0x10FFFF) 736 | { 737 | /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 738 | utf8_length = 4; 739 | first_byte_mark = 0xF0; /* 11110000 */ 740 | } 741 | else 742 | { 743 | /* invalid unicode codepoint */ 744 | goto fail; 745 | } 746 | 747 | /* encode as utf8 */ 748 | for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) 749 | { 750 | /* 10xxxxxx */ 751 | (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); 752 | codepoint >>= 6; 753 | } 754 | /* encode first byte */ 755 | if (utf8_length > 1) 756 | { 757 | (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); 758 | } 759 | else 760 | { 761 | (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); 762 | } 763 | 764 | *output_pointer += utf8_length; 765 | 766 | return sequence_length; 767 | 768 | fail: 769 | return 0; 770 | } 771 | 772 | /* Parse the input text into an unescaped cinput, and populate item. */ 773 | static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) 774 | { 775 | const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; 776 | const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; 777 | unsigned char *output_pointer = NULL; 778 | unsigned char *output = NULL; 779 | 780 | /* not a string */ 781 | if (buffer_at_offset(input_buffer)[0] != '\"') 782 | { 783 | goto fail; 784 | } 785 | 786 | { 787 | /* calculate approximate size of the output (overestimate) */ 788 | size_t allocation_length = 0; 789 | size_t skipped_bytes = 0; 790 | while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) 791 | { 792 | /* is escape sequence */ 793 | if (input_end[0] == '\\') 794 | { 795 | if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) 796 | { 797 | /* prevent buffer overflow when last input character is a backslash */ 798 | goto fail; 799 | } 800 | skipped_bytes++; 801 | input_end++; 802 | } 803 | input_end++; 804 | } 805 | if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) 806 | { 807 | goto fail; /* string ended unexpectedly */ 808 | } 809 | 810 | /* This is at most how much we need for the output */ 811 | allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; 812 | output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); 813 | if (output == NULL) 814 | { 815 | goto fail; /* allocation failure */ 816 | } 817 | } 818 | 819 | output_pointer = output; 820 | /* loop through the string literal */ 821 | while (input_pointer < input_end) 822 | { 823 | if (*input_pointer != '\\') 824 | { 825 | *output_pointer++ = *input_pointer++; 826 | } 827 | /* escape sequence */ 828 | else 829 | { 830 | unsigned char sequence_length = 2; 831 | if ((input_end - input_pointer) < 1) 832 | { 833 | goto fail; 834 | } 835 | 836 | switch (input_pointer[1]) 837 | { 838 | case 'b': 839 | *output_pointer++ = '\b'; 840 | break; 841 | case 'f': 842 | *output_pointer++ = '\f'; 843 | break; 844 | case 'n': 845 | *output_pointer++ = '\n'; 846 | break; 847 | case 'r': 848 | *output_pointer++ = '\r'; 849 | break; 850 | case 't': 851 | *output_pointer++ = '\t'; 852 | break; 853 | case '\"': 854 | case '\\': 855 | case '/': 856 | *output_pointer++ = input_pointer[1]; 857 | break; 858 | 859 | /* UTF-16 literal */ 860 | case 'u': 861 | sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); 862 | if (sequence_length == 0) 863 | { 864 | /* failed to convert UTF16-literal to UTF-8 */ 865 | goto fail; 866 | } 867 | break; 868 | 869 | default: 870 | goto fail; 871 | } 872 | input_pointer += sequence_length; 873 | } 874 | } 875 | 876 | /* zero terminate the output */ 877 | *output_pointer = '\0'; 878 | 879 | item->type = cJSON_String; 880 | item->valuestring = (char*)output; 881 | 882 | input_buffer->offset = (size_t) (input_end - input_buffer->content); 883 | input_buffer->offset++; 884 | 885 | return true; 886 | 887 | fail: 888 | if (output != NULL) 889 | { 890 | input_buffer->hooks.deallocate(output); 891 | } 892 | 893 | if (input_pointer != NULL) 894 | { 895 | input_buffer->offset = (size_t)(input_pointer - input_buffer->content); 896 | } 897 | 898 | return false; 899 | } 900 | 901 | /* Render the cstring provided to an escaped version that can be printed. */ 902 | static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) 903 | { 904 | const unsigned char *input_pointer = NULL; 905 | unsigned char *output = NULL; 906 | unsigned char *output_pointer = NULL; 907 | size_t output_length = 0; 908 | /* numbers of additional characters needed for escaping */ 909 | size_t escape_characters = 0; 910 | 911 | if (output_buffer == NULL) 912 | { 913 | return false; 914 | } 915 | 916 | /* empty string */ 917 | if (input == NULL) 918 | { 919 | output = ensure(output_buffer, sizeof("\"\"")); 920 | if (output == NULL) 921 | { 922 | return false; 923 | } 924 | strcpy((char*)output, "\"\""); 925 | 926 | return true; 927 | } 928 | 929 | /* set "flag" to 1 if something needs to be escaped */ 930 | for (input_pointer = input; *input_pointer; input_pointer++) 931 | { 932 | switch (*input_pointer) 933 | { 934 | case '\"': 935 | case '\\': 936 | case '\b': 937 | case '\f': 938 | case '\n': 939 | case '\r': 940 | case '\t': 941 | /* one character escape sequence */ 942 | escape_characters++; 943 | break; 944 | default: 945 | if (*input_pointer < 32) 946 | { 947 | /* UTF-16 escape sequence uXXXX */ 948 | escape_characters += 5; 949 | } 950 | break; 951 | } 952 | } 953 | output_length = (size_t)(input_pointer - input) + escape_characters; 954 | 955 | output = ensure(output_buffer, output_length + sizeof("\"\"")); 956 | if (output == NULL) 957 | { 958 | return false; 959 | } 960 | 961 | /* no characters have to be escaped */ 962 | if (escape_characters == 0) 963 | { 964 | output[0] = '\"'; 965 | memcpy(output + 1, input, output_length); 966 | output[output_length + 1] = '\"'; 967 | output[output_length + 2] = '\0'; 968 | 969 | return true; 970 | } 971 | 972 | output[0] = '\"'; 973 | output_pointer = output + 1; 974 | /* copy the string */ 975 | for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) 976 | { 977 | if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) 978 | { 979 | /* normal character, copy */ 980 | *output_pointer = *input_pointer; 981 | } 982 | else 983 | { 984 | /* character needs to be escaped */ 985 | *output_pointer++ = '\\'; 986 | switch (*input_pointer) 987 | { 988 | case '\\': 989 | *output_pointer = '\\'; 990 | break; 991 | case '\"': 992 | *output_pointer = '\"'; 993 | break; 994 | case '\b': 995 | *output_pointer = 'b'; 996 | break; 997 | case '\f': 998 | *output_pointer = 'f'; 999 | break; 1000 | case '\n': 1001 | *output_pointer = 'n'; 1002 | break; 1003 | case '\r': 1004 | *output_pointer = 'r'; 1005 | break; 1006 | case '\t': 1007 | *output_pointer = 't'; 1008 | break; 1009 | default: 1010 | /* escape and print as unicode codepoint */ 1011 | sprintf((char*)output_pointer, "u%04x", *input_pointer); 1012 | output_pointer += 4; 1013 | break; 1014 | } 1015 | } 1016 | } 1017 | output[output_length + 1] = '\"'; 1018 | output[output_length + 2] = '\0'; 1019 | 1020 | return true; 1021 | } 1022 | 1023 | /* Invoke print_string_ptr (which is useful) on an item. */ 1024 | static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) 1025 | { 1026 | return print_string_ptr((unsigned char*)item->valuestring, p); 1027 | } 1028 | 1029 | /* Predeclare these prototypes. */ 1030 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); 1031 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); 1032 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); 1033 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); 1034 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); 1035 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); 1036 | 1037 | /* Utility to jump whitespace and cr/lf */ 1038 | static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) 1039 | { 1040 | if ((buffer == NULL) || (buffer->content == NULL)) 1041 | { 1042 | return NULL; 1043 | } 1044 | 1045 | if (cannot_access_at_index(buffer, 0)) 1046 | { 1047 | return buffer; 1048 | } 1049 | 1050 | while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) 1051 | { 1052 | buffer->offset++; 1053 | } 1054 | 1055 | if (buffer->offset == buffer->length) 1056 | { 1057 | buffer->offset--; 1058 | } 1059 | 1060 | return buffer; 1061 | } 1062 | 1063 | /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ 1064 | static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) 1065 | { 1066 | if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) 1067 | { 1068 | return NULL; 1069 | } 1070 | 1071 | if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) 1072 | { 1073 | buffer->offset += 3; 1074 | } 1075 | 1076 | return buffer; 1077 | } 1078 | 1079 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) 1080 | { 1081 | size_t buffer_length; 1082 | 1083 | if (NULL == value) 1084 | { 1085 | return NULL; 1086 | } 1087 | 1088 | /* Adding null character size due to require_null_terminated. */ 1089 | buffer_length = strlen(value) + sizeof(""); 1090 | 1091 | return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); 1092 | } 1093 | 1094 | /* Parse an object - create a new root, and populate. */ 1095 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) 1096 | { 1097 | parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; 1098 | cJSON *item = NULL; 1099 | 1100 | /* reset error position */ 1101 | global_error.json = NULL; 1102 | global_error.position = 0; 1103 | 1104 | if (value == NULL || 0 == buffer_length) 1105 | { 1106 | goto fail; 1107 | } 1108 | 1109 | buffer.content = (const unsigned char*)value; 1110 | buffer.length = buffer_length; 1111 | buffer.offset = 0; 1112 | buffer.hooks = global_hooks; 1113 | 1114 | item = cJSON_New_Item(&global_hooks); 1115 | if (item == NULL) /* memory fail */ 1116 | { 1117 | goto fail; 1118 | } 1119 | 1120 | if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) 1121 | { 1122 | /* parse failure. ep is set. */ 1123 | goto fail; 1124 | } 1125 | 1126 | /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ 1127 | if (require_null_terminated) 1128 | { 1129 | buffer_skip_whitespace(&buffer); 1130 | if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') 1131 | { 1132 | goto fail; 1133 | } 1134 | } 1135 | if (return_parse_end) 1136 | { 1137 | *return_parse_end = (const char*)buffer_at_offset(&buffer); 1138 | } 1139 | 1140 | return item; 1141 | 1142 | fail: 1143 | if (item != NULL) 1144 | { 1145 | cJSON_Delete(item); 1146 | } 1147 | 1148 | if (value != NULL) 1149 | { 1150 | error local_error; 1151 | local_error.json = (const unsigned char*)value; 1152 | local_error.position = 0; 1153 | 1154 | if (buffer.offset < buffer.length) 1155 | { 1156 | local_error.position = buffer.offset; 1157 | } 1158 | else if (buffer.length > 0) 1159 | { 1160 | local_error.position = buffer.length - 1; 1161 | } 1162 | 1163 | if (return_parse_end != NULL) 1164 | { 1165 | *return_parse_end = (const char*)local_error.json + local_error.position; 1166 | } 1167 | 1168 | global_error = local_error; 1169 | } 1170 | 1171 | return NULL; 1172 | } 1173 | 1174 | /* Default options for cJSON_Parse */ 1175 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) 1176 | { 1177 | return cJSON_ParseWithOpts(value, 0, 0); 1178 | } 1179 | 1180 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) 1181 | { 1182 | return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); 1183 | } 1184 | 1185 | #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) 1186 | 1187 | static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) 1188 | { 1189 | static const size_t default_buffer_size = 256; 1190 | printbuffer buffer[1]; 1191 | unsigned char *printed = NULL; 1192 | 1193 | memset(buffer, 0, sizeof(buffer)); 1194 | 1195 | /* create buffer */ 1196 | buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); 1197 | buffer->length = default_buffer_size; 1198 | buffer->format = format; 1199 | buffer->hooks = *hooks; 1200 | if (buffer->buffer == NULL) 1201 | { 1202 | goto fail; 1203 | } 1204 | 1205 | /* print the value */ 1206 | if (!print_value(item, buffer)) 1207 | { 1208 | goto fail; 1209 | } 1210 | update_offset(buffer); 1211 | 1212 | /* check if reallocate is available */ 1213 | if (hooks->reallocate != NULL) 1214 | { 1215 | printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); 1216 | if (printed == NULL) { 1217 | goto fail; 1218 | } 1219 | buffer->buffer = NULL; 1220 | } 1221 | else /* otherwise copy the JSON over to a new buffer */ 1222 | { 1223 | printed = (unsigned char*) hooks->allocate(buffer->offset + 1); 1224 | if (printed == NULL) 1225 | { 1226 | goto fail; 1227 | } 1228 | memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); 1229 | printed[buffer->offset] = '\0'; /* just to be sure */ 1230 | 1231 | /* free the buffer */ 1232 | hooks->deallocate(buffer->buffer); 1233 | } 1234 | 1235 | return printed; 1236 | 1237 | fail: 1238 | if (buffer->buffer != NULL) 1239 | { 1240 | hooks->deallocate(buffer->buffer); 1241 | } 1242 | 1243 | if (printed != NULL) 1244 | { 1245 | hooks->deallocate(printed); 1246 | } 1247 | 1248 | return NULL; 1249 | } 1250 | 1251 | /* Render a cJSON item/entity/structure to text. */ 1252 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) 1253 | { 1254 | return (char*)print(item, true, &global_hooks); 1255 | } 1256 | 1257 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) 1258 | { 1259 | return (char*)print(item, false, &global_hooks); 1260 | } 1261 | 1262 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) 1263 | { 1264 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; 1265 | 1266 | if (prebuffer < 0) 1267 | { 1268 | return NULL; 1269 | } 1270 | 1271 | p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); 1272 | if (!p.buffer) 1273 | { 1274 | return NULL; 1275 | } 1276 | 1277 | p.length = (size_t)prebuffer; 1278 | p.offset = 0; 1279 | p.noalloc = false; 1280 | p.format = fmt; 1281 | p.hooks = global_hooks; 1282 | 1283 | if (!print_value(item, &p)) 1284 | { 1285 | global_hooks.deallocate(p.buffer); 1286 | return NULL; 1287 | } 1288 | 1289 | return (char*)p.buffer; 1290 | } 1291 | 1292 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) 1293 | { 1294 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; 1295 | 1296 | if ((length < 0) || (buffer == NULL)) 1297 | { 1298 | return false; 1299 | } 1300 | 1301 | p.buffer = (unsigned char*)buffer; 1302 | p.length = (size_t)length; 1303 | p.offset = 0; 1304 | p.noalloc = true; 1305 | p.format = format; 1306 | p.hooks = global_hooks; 1307 | 1308 | return print_value(item, &p); 1309 | } 1310 | 1311 | /* Parser core - when encountering text, process appropriately. */ 1312 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) 1313 | { 1314 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) 1315 | { 1316 | return false; /* no input */ 1317 | } 1318 | 1319 | /* parse the different types of values */ 1320 | /* null */ 1321 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) 1322 | { 1323 | item->type = cJSON_NULL; 1324 | input_buffer->offset += 4; 1325 | return true; 1326 | } 1327 | /* false */ 1328 | if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) 1329 | { 1330 | item->type = cJSON_False; 1331 | input_buffer->offset += 5; 1332 | return true; 1333 | } 1334 | /* true */ 1335 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) 1336 | { 1337 | item->type = cJSON_True; 1338 | item->valueint = 1; 1339 | input_buffer->offset += 4; 1340 | return true; 1341 | } 1342 | /* string */ 1343 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) 1344 | { 1345 | return parse_string(item, input_buffer); 1346 | } 1347 | /* number */ 1348 | 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')))) 1349 | { 1350 | return parse_number(item, input_buffer); 1351 | } 1352 | /* array */ 1353 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) 1354 | { 1355 | return parse_array(item, input_buffer); 1356 | } 1357 | /* object */ 1358 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) 1359 | { 1360 | return parse_object(item, input_buffer); 1361 | } 1362 | 1363 | return false; 1364 | } 1365 | 1366 | /* Render a value to text. */ 1367 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) 1368 | { 1369 | unsigned char *output = NULL; 1370 | 1371 | if ((item == NULL) || (output_buffer == NULL)) 1372 | { 1373 | return false; 1374 | } 1375 | 1376 | switch ((item->type) & 0xFF) 1377 | { 1378 | case cJSON_NULL: 1379 | output = ensure(output_buffer, 5); 1380 | if (output == NULL) 1381 | { 1382 | return false; 1383 | } 1384 | strcpy((char*)output, "null"); 1385 | return true; 1386 | 1387 | case cJSON_False: 1388 | output = ensure(output_buffer, 6); 1389 | if (output == NULL) 1390 | { 1391 | return false; 1392 | } 1393 | strcpy((char*)output, "false"); 1394 | return true; 1395 | 1396 | case cJSON_True: 1397 | output = ensure(output_buffer, 5); 1398 | if (output == NULL) 1399 | { 1400 | return false; 1401 | } 1402 | strcpy((char*)output, "true"); 1403 | return true; 1404 | 1405 | case cJSON_Number: 1406 | return print_number(item, output_buffer); 1407 | 1408 | case cJSON_Raw: 1409 | { 1410 | size_t raw_length = 0; 1411 | if (item->valuestring == NULL) 1412 | { 1413 | return false; 1414 | } 1415 | 1416 | raw_length = strlen(item->valuestring) + sizeof(""); 1417 | output = ensure(output_buffer, raw_length); 1418 | if (output == NULL) 1419 | { 1420 | return false; 1421 | } 1422 | memcpy(output, item->valuestring, raw_length); 1423 | return true; 1424 | } 1425 | 1426 | case cJSON_String: 1427 | return print_string(item, output_buffer); 1428 | 1429 | case cJSON_Array: 1430 | return print_array(item, output_buffer); 1431 | 1432 | case cJSON_Object: 1433 | return print_object(item, output_buffer); 1434 | 1435 | default: 1436 | return false; 1437 | } 1438 | } 1439 | 1440 | /* Build an array from input text. */ 1441 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) 1442 | { 1443 | cJSON *head = NULL; /* head of the linked list */ 1444 | cJSON *current_item = NULL; 1445 | 1446 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) 1447 | { 1448 | return false; /* to deeply nested */ 1449 | } 1450 | input_buffer->depth++; 1451 | 1452 | if (buffer_at_offset(input_buffer)[0] != '[') 1453 | { 1454 | /* not an array */ 1455 | goto fail; 1456 | } 1457 | 1458 | input_buffer->offset++; 1459 | buffer_skip_whitespace(input_buffer); 1460 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) 1461 | { 1462 | /* empty array */ 1463 | goto success; 1464 | } 1465 | 1466 | /* check if we skipped to the end of the buffer */ 1467 | if (cannot_access_at_index(input_buffer, 0)) 1468 | { 1469 | input_buffer->offset--; 1470 | goto fail; 1471 | } 1472 | 1473 | /* step back to character in front of the first element */ 1474 | input_buffer->offset--; 1475 | /* loop through the comma separated array elements */ 1476 | do 1477 | { 1478 | /* allocate next item */ 1479 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); 1480 | if (new_item == NULL) 1481 | { 1482 | goto fail; /* allocation failure */ 1483 | } 1484 | 1485 | /* attach next item to list */ 1486 | if (head == NULL) 1487 | { 1488 | /* start the linked list */ 1489 | current_item = head = new_item; 1490 | } 1491 | else 1492 | { 1493 | /* add to the end and advance */ 1494 | current_item->next = new_item; 1495 | new_item->prev = current_item; 1496 | current_item = new_item; 1497 | } 1498 | 1499 | /* parse next value */ 1500 | input_buffer->offset++; 1501 | buffer_skip_whitespace(input_buffer); 1502 | if (!parse_value(current_item, input_buffer)) 1503 | { 1504 | goto fail; /* failed to parse value */ 1505 | } 1506 | buffer_skip_whitespace(input_buffer); 1507 | } 1508 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 1509 | 1510 | if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') 1511 | { 1512 | goto fail; /* expected end of array */ 1513 | } 1514 | 1515 | success: 1516 | input_buffer->depth--; 1517 | 1518 | if (head != NULL) { 1519 | head->prev = current_item; 1520 | } 1521 | 1522 | item->type = cJSON_Array; 1523 | item->child = head; 1524 | 1525 | input_buffer->offset++; 1526 | 1527 | return true; 1528 | 1529 | fail: 1530 | if (head != NULL) 1531 | { 1532 | cJSON_Delete(head); 1533 | } 1534 | 1535 | return false; 1536 | } 1537 | 1538 | /* Render an array to text */ 1539 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) 1540 | { 1541 | unsigned char *output_pointer = NULL; 1542 | size_t length = 0; 1543 | cJSON *current_element = item->child; 1544 | 1545 | if (output_buffer == NULL) 1546 | { 1547 | return false; 1548 | } 1549 | 1550 | /* Compose the output array. */ 1551 | /* opening square bracket */ 1552 | output_pointer = ensure(output_buffer, 1); 1553 | if (output_pointer == NULL) 1554 | { 1555 | return false; 1556 | } 1557 | 1558 | *output_pointer = '['; 1559 | output_buffer->offset++; 1560 | output_buffer->depth++; 1561 | 1562 | while (current_element != NULL) 1563 | { 1564 | if (!print_value(current_element, output_buffer)) 1565 | { 1566 | return false; 1567 | } 1568 | update_offset(output_buffer); 1569 | if (current_element->next) 1570 | { 1571 | length = (size_t) (output_buffer->format ? 2 : 1); 1572 | output_pointer = ensure(output_buffer, length + 1); 1573 | if (output_pointer == NULL) 1574 | { 1575 | return false; 1576 | } 1577 | *output_pointer++ = ','; 1578 | if(output_buffer->format) 1579 | { 1580 | *output_pointer++ = ' '; 1581 | } 1582 | *output_pointer = '\0'; 1583 | output_buffer->offset += length; 1584 | } 1585 | current_element = current_element->next; 1586 | } 1587 | 1588 | output_pointer = ensure(output_buffer, 2); 1589 | if (output_pointer == NULL) 1590 | { 1591 | return false; 1592 | } 1593 | *output_pointer++ = ']'; 1594 | *output_pointer = '\0'; 1595 | output_buffer->depth--; 1596 | 1597 | return true; 1598 | } 1599 | 1600 | /* Build an object from the text. */ 1601 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) 1602 | { 1603 | cJSON *head = NULL; /* linked list head */ 1604 | cJSON *current_item = NULL; 1605 | 1606 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) 1607 | { 1608 | return false; /* to deeply nested */ 1609 | } 1610 | input_buffer->depth++; 1611 | 1612 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) 1613 | { 1614 | goto fail; /* not an object */ 1615 | } 1616 | 1617 | input_buffer->offset++; 1618 | buffer_skip_whitespace(input_buffer); 1619 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) 1620 | { 1621 | goto success; /* empty object */ 1622 | } 1623 | 1624 | /* check if we skipped to the end of the buffer */ 1625 | if (cannot_access_at_index(input_buffer, 0)) 1626 | { 1627 | input_buffer->offset--; 1628 | goto fail; 1629 | } 1630 | 1631 | /* step back to character in front of the first element */ 1632 | input_buffer->offset--; 1633 | /* loop through the comma separated array elements */ 1634 | do 1635 | { 1636 | /* allocate next item */ 1637 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); 1638 | if (new_item == NULL) 1639 | { 1640 | goto fail; /* allocation failure */ 1641 | } 1642 | 1643 | /* attach next item to list */ 1644 | if (head == NULL) 1645 | { 1646 | /* start the linked list */ 1647 | current_item = head = new_item; 1648 | } 1649 | else 1650 | { 1651 | /* add to the end and advance */ 1652 | current_item->next = new_item; 1653 | new_item->prev = current_item; 1654 | current_item = new_item; 1655 | } 1656 | 1657 | /* parse the name of the child */ 1658 | input_buffer->offset++; 1659 | buffer_skip_whitespace(input_buffer); 1660 | if (!parse_string(current_item, input_buffer)) 1661 | { 1662 | goto fail; /* failed to parse name */ 1663 | } 1664 | buffer_skip_whitespace(input_buffer); 1665 | 1666 | /* swap valuestring and string, because we parsed the name */ 1667 | current_item->string = current_item->valuestring; 1668 | current_item->valuestring = NULL; 1669 | 1670 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) 1671 | { 1672 | goto fail; /* invalid object */ 1673 | } 1674 | 1675 | /* parse the value */ 1676 | input_buffer->offset++; 1677 | buffer_skip_whitespace(input_buffer); 1678 | if (!parse_value(current_item, input_buffer)) 1679 | { 1680 | goto fail; /* failed to parse value */ 1681 | } 1682 | buffer_skip_whitespace(input_buffer); 1683 | } 1684 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 1685 | 1686 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) 1687 | { 1688 | goto fail; /* expected end of object */ 1689 | } 1690 | 1691 | success: 1692 | input_buffer->depth--; 1693 | 1694 | if (head != NULL) { 1695 | head->prev = current_item; 1696 | } 1697 | 1698 | item->type = cJSON_Object; 1699 | item->child = head; 1700 | 1701 | input_buffer->offset++; 1702 | return true; 1703 | 1704 | fail: 1705 | if (head != NULL) 1706 | { 1707 | cJSON_Delete(head); 1708 | } 1709 | 1710 | return false; 1711 | } 1712 | 1713 | /* Render an object to text. */ 1714 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) 1715 | { 1716 | unsigned char *output_pointer = NULL; 1717 | size_t length = 0; 1718 | cJSON *current_item = item->child; 1719 | 1720 | if (output_buffer == NULL) 1721 | { 1722 | return false; 1723 | } 1724 | 1725 | /* Compose the output: */ 1726 | length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ 1727 | output_pointer = ensure(output_buffer, length + 1); 1728 | if (output_pointer == NULL) 1729 | { 1730 | return false; 1731 | } 1732 | 1733 | *output_pointer++ = '{'; 1734 | output_buffer->depth++; 1735 | if (output_buffer->format) 1736 | { 1737 | *output_pointer++ = '\n'; 1738 | } 1739 | output_buffer->offset += length; 1740 | 1741 | while (current_item) 1742 | { 1743 | if (output_buffer->format) 1744 | { 1745 | size_t i; 1746 | output_pointer = ensure(output_buffer, output_buffer->depth); 1747 | if (output_pointer == NULL) 1748 | { 1749 | return false; 1750 | } 1751 | for (i = 0; i < output_buffer->depth; i++) 1752 | { 1753 | *output_pointer++ = '\t'; 1754 | } 1755 | output_buffer->offset += output_buffer->depth; 1756 | } 1757 | 1758 | /* print key */ 1759 | if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) 1760 | { 1761 | return false; 1762 | } 1763 | update_offset(output_buffer); 1764 | 1765 | length = (size_t) (output_buffer->format ? 2 : 1); 1766 | output_pointer = ensure(output_buffer, length); 1767 | if (output_pointer == NULL) 1768 | { 1769 | return false; 1770 | } 1771 | *output_pointer++ = ':'; 1772 | if (output_buffer->format) 1773 | { 1774 | *output_pointer++ = '\t'; 1775 | } 1776 | output_buffer->offset += length; 1777 | 1778 | /* print value */ 1779 | if (!print_value(current_item, output_buffer)) 1780 | { 1781 | return false; 1782 | } 1783 | update_offset(output_buffer); 1784 | 1785 | /* print comma if not last */ 1786 | length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); 1787 | output_pointer = ensure(output_buffer, length + 1); 1788 | if (output_pointer == NULL) 1789 | { 1790 | return false; 1791 | } 1792 | if (current_item->next) 1793 | { 1794 | *output_pointer++ = ','; 1795 | } 1796 | 1797 | if (output_buffer->format) 1798 | { 1799 | *output_pointer++ = '\n'; 1800 | } 1801 | *output_pointer = '\0'; 1802 | output_buffer->offset += length; 1803 | 1804 | current_item = current_item->next; 1805 | } 1806 | 1807 | output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); 1808 | if (output_pointer == NULL) 1809 | { 1810 | return false; 1811 | } 1812 | if (output_buffer->format) 1813 | { 1814 | size_t i; 1815 | for (i = 0; i < (output_buffer->depth - 1); i++) 1816 | { 1817 | *output_pointer++ = '\t'; 1818 | } 1819 | } 1820 | *output_pointer++ = '}'; 1821 | *output_pointer = '\0'; 1822 | output_buffer->depth--; 1823 | 1824 | return true; 1825 | } 1826 | 1827 | /* Get Array size/item / object item. */ 1828 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) 1829 | { 1830 | cJSON *child = NULL; 1831 | size_t size = 0; 1832 | 1833 | if (array == NULL) 1834 | { 1835 | return 0; 1836 | } 1837 | 1838 | child = array->child; 1839 | 1840 | while(child != NULL) 1841 | { 1842 | size++; 1843 | child = child->next; 1844 | } 1845 | 1846 | /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ 1847 | 1848 | return (int)size; 1849 | } 1850 | 1851 | static cJSON* get_array_item(const cJSON *array, size_t index) 1852 | { 1853 | cJSON *current_child = NULL; 1854 | 1855 | if (array == NULL) 1856 | { 1857 | return NULL; 1858 | } 1859 | 1860 | current_child = array->child; 1861 | while ((current_child != NULL) && (index > 0)) 1862 | { 1863 | index--; 1864 | current_child = current_child->next; 1865 | } 1866 | 1867 | return current_child; 1868 | } 1869 | 1870 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) 1871 | { 1872 | if (index < 0) 1873 | { 1874 | return NULL; 1875 | } 1876 | 1877 | return get_array_item(array, (size_t)index); 1878 | } 1879 | 1880 | static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) 1881 | { 1882 | cJSON *current_element = NULL; 1883 | 1884 | if ((object == NULL) || (name == NULL)) 1885 | { 1886 | return NULL; 1887 | } 1888 | 1889 | current_element = object->child; 1890 | if (case_sensitive) 1891 | { 1892 | while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) 1893 | { 1894 | current_element = current_element->next; 1895 | } 1896 | } 1897 | else 1898 | { 1899 | while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) 1900 | { 1901 | current_element = current_element->next; 1902 | } 1903 | } 1904 | 1905 | if ((current_element == NULL) || (current_element->string == NULL)) { 1906 | return NULL; 1907 | } 1908 | 1909 | return current_element; 1910 | } 1911 | 1912 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) 1913 | { 1914 | return get_object_item(object, string, false); 1915 | } 1916 | 1917 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) 1918 | { 1919 | return get_object_item(object, string, true); 1920 | } 1921 | 1922 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) 1923 | { 1924 | return cJSON_GetObjectItem(object, string) ? 1 : 0; 1925 | } 1926 | 1927 | /* Utility for array list handling. */ 1928 | static void suffix_object(cJSON *prev, cJSON *item) 1929 | { 1930 | prev->next = item; 1931 | item->prev = prev; 1932 | } 1933 | 1934 | /* Utility for handling references. */ 1935 | static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) 1936 | { 1937 | cJSON *reference = NULL; 1938 | if (item == NULL) 1939 | { 1940 | return NULL; 1941 | } 1942 | 1943 | reference = cJSON_New_Item(hooks); 1944 | if (reference == NULL) 1945 | { 1946 | return NULL; 1947 | } 1948 | 1949 | memcpy(reference, item, sizeof(cJSON)); 1950 | reference->string = NULL; 1951 | reference->type |= cJSON_IsReference; 1952 | reference->next = reference->prev = NULL; 1953 | return reference; 1954 | } 1955 | 1956 | static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) 1957 | { 1958 | cJSON *child = NULL; 1959 | 1960 | if ((item == NULL) || (array == NULL) || (array == item)) 1961 | { 1962 | return false; 1963 | } 1964 | 1965 | child = array->child; 1966 | /* 1967 | * To find the last item in array quickly, we use prev in array 1968 | */ 1969 | if (child == NULL) 1970 | { 1971 | /* list is empty, start new one */ 1972 | array->child = item; 1973 | item->prev = item; 1974 | item->next = NULL; 1975 | } 1976 | else 1977 | { 1978 | /* append to the end */ 1979 | if (child->prev) 1980 | { 1981 | suffix_object(child->prev, item); 1982 | array->child->prev = item; 1983 | } 1984 | } 1985 | 1986 | return true; 1987 | } 1988 | 1989 | /* Add item to array/object. */ 1990 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) 1991 | { 1992 | return add_item_to_array(array, item); 1993 | } 1994 | 1995 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 1996 | #pragma GCC diagnostic push 1997 | #endif 1998 | #ifdef __GNUC__ 1999 | #pragma GCC diagnostic ignored "-Wcast-qual" 2000 | #endif 2001 | /* helper function to cast away const */ 2002 | static void* cast_away_const(const void* string) 2003 | { 2004 | return (void*)string; 2005 | } 2006 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 2007 | #pragma GCC diagnostic pop 2008 | #endif 2009 | 2010 | 2011 | 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) 2012 | { 2013 | char *new_key = NULL; 2014 | int new_type = cJSON_Invalid; 2015 | 2016 | if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) 2017 | { 2018 | return false; 2019 | } 2020 | 2021 | if (constant_key) 2022 | { 2023 | new_key = (char*)cast_away_const(string); 2024 | new_type = item->type | cJSON_StringIsConst; 2025 | } 2026 | else 2027 | { 2028 | new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); 2029 | if (new_key == NULL) 2030 | { 2031 | return false; 2032 | } 2033 | 2034 | new_type = item->type & ~cJSON_StringIsConst; 2035 | } 2036 | 2037 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) 2038 | { 2039 | hooks->deallocate(item->string); 2040 | } 2041 | 2042 | item->string = new_key; 2043 | item->type = new_type; 2044 | 2045 | return add_item_to_array(object, item); 2046 | } 2047 | 2048 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) 2049 | { 2050 | return add_item_to_object(object, string, item, &global_hooks, false); 2051 | } 2052 | 2053 | /* Add an item to an object with constant string as key */ 2054 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) 2055 | { 2056 | return add_item_to_object(object, string, item, &global_hooks, true); 2057 | } 2058 | 2059 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) 2060 | { 2061 | if (array == NULL) 2062 | { 2063 | return false; 2064 | } 2065 | 2066 | return add_item_to_array(array, create_reference(item, &global_hooks)); 2067 | } 2068 | 2069 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) 2070 | { 2071 | if ((object == NULL) || (string == NULL)) 2072 | { 2073 | return false; 2074 | } 2075 | 2076 | return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); 2077 | } 2078 | 2079 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) 2080 | { 2081 | cJSON *null = cJSON_CreateNull(); 2082 | if (add_item_to_object(object, name, null, &global_hooks, false)) 2083 | { 2084 | return null; 2085 | } 2086 | 2087 | cJSON_Delete(null); 2088 | return NULL; 2089 | } 2090 | 2091 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) 2092 | { 2093 | cJSON *true_item = cJSON_CreateTrue(); 2094 | if (add_item_to_object(object, name, true_item, &global_hooks, false)) 2095 | { 2096 | return true_item; 2097 | } 2098 | 2099 | cJSON_Delete(true_item); 2100 | return NULL; 2101 | } 2102 | 2103 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) 2104 | { 2105 | cJSON *false_item = cJSON_CreateFalse(); 2106 | if (add_item_to_object(object, name, false_item, &global_hooks, false)) 2107 | { 2108 | return false_item; 2109 | } 2110 | 2111 | cJSON_Delete(false_item); 2112 | return NULL; 2113 | } 2114 | 2115 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) 2116 | { 2117 | cJSON *bool_item = cJSON_CreateBool(boolean); 2118 | if (add_item_to_object(object, name, bool_item, &global_hooks, false)) 2119 | { 2120 | return bool_item; 2121 | } 2122 | 2123 | cJSON_Delete(bool_item); 2124 | return NULL; 2125 | } 2126 | 2127 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) 2128 | { 2129 | cJSON *number_item = cJSON_CreateNumber(number); 2130 | if (add_item_to_object(object, name, number_item, &global_hooks, false)) 2131 | { 2132 | return number_item; 2133 | } 2134 | 2135 | cJSON_Delete(number_item); 2136 | return NULL; 2137 | } 2138 | 2139 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) 2140 | { 2141 | cJSON *string_item = cJSON_CreateString(string); 2142 | if (add_item_to_object(object, name, string_item, &global_hooks, false)) 2143 | { 2144 | return string_item; 2145 | } 2146 | 2147 | cJSON_Delete(string_item); 2148 | return NULL; 2149 | } 2150 | 2151 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) 2152 | { 2153 | cJSON *raw_item = cJSON_CreateRaw(raw); 2154 | if (add_item_to_object(object, name, raw_item, &global_hooks, false)) 2155 | { 2156 | return raw_item; 2157 | } 2158 | 2159 | cJSON_Delete(raw_item); 2160 | return NULL; 2161 | } 2162 | 2163 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) 2164 | { 2165 | cJSON *object_item = cJSON_CreateObject(); 2166 | if (add_item_to_object(object, name, object_item, &global_hooks, false)) 2167 | { 2168 | return object_item; 2169 | } 2170 | 2171 | cJSON_Delete(object_item); 2172 | return NULL; 2173 | } 2174 | 2175 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) 2176 | { 2177 | cJSON *array = cJSON_CreateArray(); 2178 | if (add_item_to_object(object, name, array, &global_hooks, false)) 2179 | { 2180 | return array; 2181 | } 2182 | 2183 | cJSON_Delete(array); 2184 | return NULL; 2185 | } 2186 | 2187 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) 2188 | { 2189 | if ((parent == NULL) || (item == NULL)) 2190 | { 2191 | return NULL; 2192 | } 2193 | 2194 | if (item != parent->child) 2195 | { 2196 | /* not the first element */ 2197 | item->prev->next = item->next; 2198 | } 2199 | if (item->next != NULL) 2200 | { 2201 | /* not the last element */ 2202 | item->next->prev = item->prev; 2203 | } 2204 | 2205 | if (item == parent->child) 2206 | { 2207 | /* first element */ 2208 | parent->child = item->next; 2209 | } 2210 | else if (item->next == NULL) 2211 | { 2212 | /* last element */ 2213 | parent->child->prev = item->prev; 2214 | } 2215 | 2216 | /* make sure the detached item doesn't point anywhere anymore */ 2217 | item->prev = NULL; 2218 | item->next = NULL; 2219 | 2220 | return item; 2221 | } 2222 | 2223 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) 2224 | { 2225 | if (which < 0) 2226 | { 2227 | return NULL; 2228 | } 2229 | 2230 | return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); 2231 | } 2232 | 2233 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) 2234 | { 2235 | cJSON_Delete(cJSON_DetachItemFromArray(array, which)); 2236 | } 2237 | 2238 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) 2239 | { 2240 | cJSON *to_detach = cJSON_GetObjectItem(object, string); 2241 | 2242 | return cJSON_DetachItemViaPointer(object, to_detach); 2243 | } 2244 | 2245 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) 2246 | { 2247 | cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); 2248 | 2249 | return cJSON_DetachItemViaPointer(object, to_detach); 2250 | } 2251 | 2252 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) 2253 | { 2254 | cJSON_Delete(cJSON_DetachItemFromObject(object, string)); 2255 | } 2256 | 2257 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) 2258 | { 2259 | cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); 2260 | } 2261 | 2262 | /* Replace array/object items with new ones. */ 2263 | CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) 2264 | { 2265 | cJSON *after_inserted = NULL; 2266 | 2267 | if (which < 0) 2268 | { 2269 | return false; 2270 | } 2271 | 2272 | after_inserted = get_array_item(array, (size_t)which); 2273 | if (after_inserted == NULL) 2274 | { 2275 | return add_item_to_array(array, newitem); 2276 | } 2277 | 2278 | newitem->next = after_inserted; 2279 | newitem->prev = after_inserted->prev; 2280 | after_inserted->prev = newitem; 2281 | if (after_inserted == array->child) 2282 | { 2283 | array->child = newitem; 2284 | } 2285 | else 2286 | { 2287 | newitem->prev->next = newitem; 2288 | } 2289 | return true; 2290 | } 2291 | 2292 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) 2293 | { 2294 | if ((parent == NULL) || (replacement == NULL) || (item == NULL)) 2295 | { 2296 | return false; 2297 | } 2298 | 2299 | if (replacement == item) 2300 | { 2301 | return true; 2302 | } 2303 | 2304 | replacement->next = item->next; 2305 | replacement->prev = item->prev; 2306 | 2307 | if (replacement->next != NULL) 2308 | { 2309 | replacement->next->prev = replacement; 2310 | } 2311 | if (parent->child == item) 2312 | { 2313 | if (parent->child->prev == parent->child) 2314 | { 2315 | replacement->prev = replacement; 2316 | } 2317 | parent->child = replacement; 2318 | } 2319 | else 2320 | { /* 2321 | * To find the last item in array quickly, we use prev in array. 2322 | * We can't modify the last item's next pointer where this item was the parent's child 2323 | */ 2324 | if (replacement->prev != NULL) 2325 | { 2326 | replacement->prev->next = replacement; 2327 | } 2328 | if (replacement->next == NULL) 2329 | { 2330 | parent->child->prev = replacement; 2331 | } 2332 | } 2333 | 2334 | item->next = NULL; 2335 | item->prev = NULL; 2336 | cJSON_Delete(item); 2337 | 2338 | return true; 2339 | } 2340 | 2341 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) 2342 | { 2343 | if (which < 0) 2344 | { 2345 | return false; 2346 | } 2347 | 2348 | return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); 2349 | } 2350 | 2351 | static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) 2352 | { 2353 | if ((replacement == NULL) || (string == NULL)) 2354 | { 2355 | return false; 2356 | } 2357 | 2358 | /* replace the name in the replacement */ 2359 | if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) 2360 | { 2361 | cJSON_free(replacement->string); 2362 | } 2363 | replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); 2364 | if (replacement->string == NULL) 2365 | { 2366 | return false; 2367 | } 2368 | 2369 | replacement->type &= ~cJSON_StringIsConst; 2370 | 2371 | return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); 2372 | } 2373 | 2374 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) 2375 | { 2376 | return replace_item_in_object(object, string, newitem, false); 2377 | } 2378 | 2379 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) 2380 | { 2381 | return replace_item_in_object(object, string, newitem, true); 2382 | } 2383 | 2384 | /* Create basic types: */ 2385 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) 2386 | { 2387 | cJSON *item = cJSON_New_Item(&global_hooks); 2388 | if(item) 2389 | { 2390 | item->type = cJSON_NULL; 2391 | } 2392 | 2393 | return item; 2394 | } 2395 | 2396 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) 2397 | { 2398 | cJSON *item = cJSON_New_Item(&global_hooks); 2399 | if(item) 2400 | { 2401 | item->type = cJSON_True; 2402 | } 2403 | 2404 | return item; 2405 | } 2406 | 2407 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) 2408 | { 2409 | cJSON *item = cJSON_New_Item(&global_hooks); 2410 | if(item) 2411 | { 2412 | item->type = cJSON_False; 2413 | } 2414 | 2415 | return item; 2416 | } 2417 | 2418 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) 2419 | { 2420 | cJSON *item = cJSON_New_Item(&global_hooks); 2421 | if(item) 2422 | { 2423 | item->type = boolean ? cJSON_True : cJSON_False; 2424 | } 2425 | 2426 | return item; 2427 | } 2428 | 2429 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) 2430 | { 2431 | cJSON *item = cJSON_New_Item(&global_hooks); 2432 | if(item) 2433 | { 2434 | item->type = cJSON_Number; 2435 | item->valuedouble = num; 2436 | 2437 | /* use saturation in case of overflow */ 2438 | if (num >= INT_MAX) 2439 | { 2440 | item->valueint = INT_MAX; 2441 | } 2442 | else if (num <= (double)INT_MIN) 2443 | { 2444 | item->valueint = INT_MIN; 2445 | } 2446 | else 2447 | { 2448 | item->valueint = (int)num; 2449 | } 2450 | } 2451 | 2452 | return item; 2453 | } 2454 | 2455 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) 2456 | { 2457 | cJSON *item = cJSON_New_Item(&global_hooks); 2458 | if(item) 2459 | { 2460 | item->type = cJSON_String; 2461 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); 2462 | if(!item->valuestring) 2463 | { 2464 | cJSON_Delete(item); 2465 | return NULL; 2466 | } 2467 | } 2468 | 2469 | return item; 2470 | } 2471 | 2472 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) 2473 | { 2474 | cJSON *item = cJSON_New_Item(&global_hooks); 2475 | if (item != NULL) 2476 | { 2477 | item->type = cJSON_String | cJSON_IsReference; 2478 | item->valuestring = (char*)cast_away_const(string); 2479 | } 2480 | 2481 | return item; 2482 | } 2483 | 2484 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) 2485 | { 2486 | cJSON *item = cJSON_New_Item(&global_hooks); 2487 | if (item != NULL) { 2488 | item->type = cJSON_Object | cJSON_IsReference; 2489 | item->child = (cJSON*)cast_away_const(child); 2490 | } 2491 | 2492 | return item; 2493 | } 2494 | 2495 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { 2496 | cJSON *item = cJSON_New_Item(&global_hooks); 2497 | if (item != NULL) { 2498 | item->type = cJSON_Array | cJSON_IsReference; 2499 | item->child = (cJSON*)cast_away_const(child); 2500 | } 2501 | 2502 | return item; 2503 | } 2504 | 2505 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) 2506 | { 2507 | cJSON *item = cJSON_New_Item(&global_hooks); 2508 | if(item) 2509 | { 2510 | item->type = cJSON_Raw; 2511 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); 2512 | if(!item->valuestring) 2513 | { 2514 | cJSON_Delete(item); 2515 | return NULL; 2516 | } 2517 | } 2518 | 2519 | return item; 2520 | } 2521 | 2522 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) 2523 | { 2524 | cJSON *item = cJSON_New_Item(&global_hooks); 2525 | if(item) 2526 | { 2527 | item->type=cJSON_Array; 2528 | } 2529 | 2530 | return item; 2531 | } 2532 | 2533 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) 2534 | { 2535 | cJSON *item = cJSON_New_Item(&global_hooks); 2536 | if (item) 2537 | { 2538 | item->type = cJSON_Object; 2539 | } 2540 | 2541 | return item; 2542 | } 2543 | 2544 | /* Create Arrays: */ 2545 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) 2546 | { 2547 | size_t i = 0; 2548 | cJSON *n = NULL; 2549 | cJSON *p = NULL; 2550 | cJSON *a = NULL; 2551 | 2552 | if ((count < 0) || (numbers == NULL)) 2553 | { 2554 | return NULL; 2555 | } 2556 | 2557 | a = cJSON_CreateArray(); 2558 | 2559 | for(i = 0; a && (i < (size_t)count); i++) 2560 | { 2561 | n = cJSON_CreateNumber(numbers[i]); 2562 | if (!n) 2563 | { 2564 | cJSON_Delete(a); 2565 | return NULL; 2566 | } 2567 | if(!i) 2568 | { 2569 | a->child = n; 2570 | } 2571 | else 2572 | { 2573 | suffix_object(p, n); 2574 | } 2575 | p = n; 2576 | } 2577 | 2578 | if (a && a->child) { 2579 | a->child->prev = n; 2580 | } 2581 | 2582 | return a; 2583 | } 2584 | 2585 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) 2586 | { 2587 | size_t i = 0; 2588 | cJSON *n = NULL; 2589 | cJSON *p = NULL; 2590 | cJSON *a = NULL; 2591 | 2592 | if ((count < 0) || (numbers == NULL)) 2593 | { 2594 | return NULL; 2595 | } 2596 | 2597 | a = cJSON_CreateArray(); 2598 | 2599 | for(i = 0; a && (i < (size_t)count); i++) 2600 | { 2601 | n = cJSON_CreateNumber((double)numbers[i]); 2602 | if(!n) 2603 | { 2604 | cJSON_Delete(a); 2605 | return NULL; 2606 | } 2607 | if(!i) 2608 | { 2609 | a->child = n; 2610 | } 2611 | else 2612 | { 2613 | suffix_object(p, n); 2614 | } 2615 | p = n; 2616 | } 2617 | 2618 | if (a && a->child) { 2619 | a->child->prev = n; 2620 | } 2621 | 2622 | return a; 2623 | } 2624 | 2625 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) 2626 | { 2627 | size_t i = 0; 2628 | cJSON *n = NULL; 2629 | cJSON *p = NULL; 2630 | cJSON *a = NULL; 2631 | 2632 | if ((count < 0) || (numbers == NULL)) 2633 | { 2634 | return NULL; 2635 | } 2636 | 2637 | a = cJSON_CreateArray(); 2638 | 2639 | for(i = 0; a && (i < (size_t)count); i++) 2640 | { 2641 | n = cJSON_CreateNumber(numbers[i]); 2642 | if(!n) 2643 | { 2644 | cJSON_Delete(a); 2645 | return NULL; 2646 | } 2647 | if(!i) 2648 | { 2649 | a->child = n; 2650 | } 2651 | else 2652 | { 2653 | suffix_object(p, n); 2654 | } 2655 | p = n; 2656 | } 2657 | 2658 | if (a && a->child) { 2659 | a->child->prev = n; 2660 | } 2661 | 2662 | return a; 2663 | } 2664 | 2665 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) 2666 | { 2667 | size_t i = 0; 2668 | cJSON *n = NULL; 2669 | cJSON *p = NULL; 2670 | cJSON *a = NULL; 2671 | 2672 | if ((count < 0) || (strings == NULL)) 2673 | { 2674 | return NULL; 2675 | } 2676 | 2677 | a = cJSON_CreateArray(); 2678 | 2679 | for (i = 0; a && (i < (size_t)count); i++) 2680 | { 2681 | n = cJSON_CreateString(strings[i]); 2682 | if(!n) 2683 | { 2684 | cJSON_Delete(a); 2685 | return NULL; 2686 | } 2687 | if(!i) 2688 | { 2689 | a->child = n; 2690 | } 2691 | else 2692 | { 2693 | suffix_object(p,n); 2694 | } 2695 | p = n; 2696 | } 2697 | 2698 | if (a && a->child) { 2699 | a->child->prev = n; 2700 | } 2701 | 2702 | return a; 2703 | } 2704 | 2705 | /* Duplication */ 2706 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) 2707 | { 2708 | cJSON *newitem = NULL; 2709 | cJSON *child = NULL; 2710 | cJSON *next = NULL; 2711 | cJSON *newchild = NULL; 2712 | 2713 | /* Bail on bad ptr */ 2714 | if (!item) 2715 | { 2716 | goto fail; 2717 | } 2718 | /* Create new item */ 2719 | newitem = cJSON_New_Item(&global_hooks); 2720 | if (!newitem) 2721 | { 2722 | goto fail; 2723 | } 2724 | /* Copy over all vars */ 2725 | newitem->type = item->type & (~cJSON_IsReference); 2726 | newitem->valueint = item->valueint; 2727 | newitem->valuedouble = item->valuedouble; 2728 | if (item->valuestring) 2729 | { 2730 | newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); 2731 | if (!newitem->valuestring) 2732 | { 2733 | goto fail; 2734 | } 2735 | } 2736 | if (item->string) 2737 | { 2738 | newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); 2739 | if (!newitem->string) 2740 | { 2741 | goto fail; 2742 | } 2743 | } 2744 | /* If non-recursive, then we're done! */ 2745 | if (!recurse) 2746 | { 2747 | return newitem; 2748 | } 2749 | /* Walk the ->next chain for the child. */ 2750 | child = item->child; 2751 | while (child != NULL) 2752 | { 2753 | newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ 2754 | if (!newchild) 2755 | { 2756 | goto fail; 2757 | } 2758 | if (next != NULL) 2759 | { 2760 | /* If newitem->child already set, then crosswire ->prev and ->next and move on */ 2761 | next->next = newchild; 2762 | newchild->prev = next; 2763 | next = newchild; 2764 | } 2765 | else 2766 | { 2767 | /* Set newitem->child and move to it */ 2768 | newitem->child = newchild; 2769 | next = newchild; 2770 | } 2771 | child = child->next; 2772 | } 2773 | if (newitem && newitem->child) 2774 | { 2775 | newitem->child->prev = newchild; 2776 | } 2777 | 2778 | return newitem; 2779 | 2780 | fail: 2781 | if (newitem != NULL) 2782 | { 2783 | cJSON_Delete(newitem); 2784 | } 2785 | 2786 | return NULL; 2787 | } 2788 | 2789 | static void skip_oneline_comment(char **input) 2790 | { 2791 | *input += static_strlen("//"); 2792 | 2793 | for (; (*input)[0] != '\0'; ++(*input)) 2794 | { 2795 | if ((*input)[0] == '\n') { 2796 | *input += static_strlen("\n"); 2797 | return; 2798 | } 2799 | } 2800 | } 2801 | 2802 | static void skip_multiline_comment(char **input) 2803 | { 2804 | *input += static_strlen("/*"); 2805 | 2806 | for (; (*input)[0] != '\0'; ++(*input)) 2807 | { 2808 | if (((*input)[0] == '*') && ((*input)[1] == '/')) 2809 | { 2810 | *input += static_strlen("*/"); 2811 | return; 2812 | } 2813 | } 2814 | } 2815 | 2816 | static void minify_string(char **input, char **output) { 2817 | (*output)[0] = (*input)[0]; 2818 | *input += static_strlen("\""); 2819 | *output += static_strlen("\""); 2820 | 2821 | 2822 | for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { 2823 | (*output)[0] = (*input)[0]; 2824 | 2825 | if ((*input)[0] == '\"') { 2826 | (*output)[0] = '\"'; 2827 | *input += static_strlen("\""); 2828 | *output += static_strlen("\""); 2829 | return; 2830 | } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { 2831 | (*output)[1] = (*input)[1]; 2832 | *input += static_strlen("\""); 2833 | *output += static_strlen("\""); 2834 | } 2835 | } 2836 | } 2837 | 2838 | CJSON_PUBLIC(void) cJSON_Minify(char *json) 2839 | { 2840 | char *into = json; 2841 | 2842 | if (json == NULL) 2843 | { 2844 | return; 2845 | } 2846 | 2847 | while (json[0] != '\0') 2848 | { 2849 | switch (json[0]) 2850 | { 2851 | case ' ': 2852 | case '\t': 2853 | case '\r': 2854 | case '\n': 2855 | json++; 2856 | break; 2857 | 2858 | case '/': 2859 | if (json[1] == '/') 2860 | { 2861 | skip_oneline_comment(&json); 2862 | } 2863 | else if (json[1] == '*') 2864 | { 2865 | skip_multiline_comment(&json); 2866 | } else { 2867 | json++; 2868 | } 2869 | break; 2870 | 2871 | case '\"': 2872 | minify_string(&json, (char**)&into); 2873 | break; 2874 | 2875 | default: 2876 | into[0] = json[0]; 2877 | json++; 2878 | into++; 2879 | } 2880 | } 2881 | 2882 | /* and null-terminate. */ 2883 | *into = '\0'; 2884 | } 2885 | 2886 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) 2887 | { 2888 | if (item == NULL) 2889 | { 2890 | return false; 2891 | } 2892 | 2893 | return (item->type & 0xFF) == cJSON_Invalid; 2894 | } 2895 | 2896 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) 2897 | { 2898 | if (item == NULL) 2899 | { 2900 | return false; 2901 | } 2902 | 2903 | return (item->type & 0xFF) == cJSON_False; 2904 | } 2905 | 2906 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) 2907 | { 2908 | if (item == NULL) 2909 | { 2910 | return false; 2911 | } 2912 | 2913 | return (item->type & 0xff) == cJSON_True; 2914 | } 2915 | 2916 | 2917 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) 2918 | { 2919 | if (item == NULL) 2920 | { 2921 | return false; 2922 | } 2923 | 2924 | return (item->type & (cJSON_True | cJSON_False)) != 0; 2925 | } 2926 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) 2927 | { 2928 | if (item == NULL) 2929 | { 2930 | return false; 2931 | } 2932 | 2933 | return (item->type & 0xFF) == cJSON_NULL; 2934 | } 2935 | 2936 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) 2937 | { 2938 | if (item == NULL) 2939 | { 2940 | return false; 2941 | } 2942 | 2943 | return (item->type & 0xFF) == cJSON_Number; 2944 | } 2945 | 2946 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) 2947 | { 2948 | if (item == NULL) 2949 | { 2950 | return false; 2951 | } 2952 | 2953 | return (item->type & 0xFF) == cJSON_String; 2954 | } 2955 | 2956 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) 2957 | { 2958 | if (item == NULL) 2959 | { 2960 | return false; 2961 | } 2962 | 2963 | return (item->type & 0xFF) == cJSON_Array; 2964 | } 2965 | 2966 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) 2967 | { 2968 | if (item == NULL) 2969 | { 2970 | return false; 2971 | } 2972 | 2973 | return (item->type & 0xFF) == cJSON_Object; 2974 | } 2975 | 2976 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) 2977 | { 2978 | if (item == NULL) 2979 | { 2980 | return false; 2981 | } 2982 | 2983 | return (item->type & 0xFF) == cJSON_Raw; 2984 | } 2985 | 2986 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) 2987 | { 2988 | if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) 2989 | { 2990 | return false; 2991 | } 2992 | 2993 | /* check if type is valid */ 2994 | switch (a->type & 0xFF) 2995 | { 2996 | case cJSON_False: 2997 | case cJSON_True: 2998 | case cJSON_NULL: 2999 | case cJSON_Number: 3000 | case cJSON_String: 3001 | case cJSON_Raw: 3002 | case cJSON_Array: 3003 | case cJSON_Object: 3004 | break; 3005 | 3006 | default: 3007 | return false; 3008 | } 3009 | 3010 | /* identical objects are equal */ 3011 | if (a == b) 3012 | { 3013 | return true; 3014 | } 3015 | 3016 | switch (a->type & 0xFF) 3017 | { 3018 | /* in these cases and equal type is enough */ 3019 | case cJSON_False: 3020 | case cJSON_True: 3021 | case cJSON_NULL: 3022 | return true; 3023 | 3024 | case cJSON_Number: 3025 | if (compare_double(a->valuedouble, b->valuedouble)) 3026 | { 3027 | return true; 3028 | } 3029 | return false; 3030 | 3031 | case cJSON_String: 3032 | case cJSON_Raw: 3033 | if ((a->valuestring == NULL) || (b->valuestring == NULL)) 3034 | { 3035 | return false; 3036 | } 3037 | if (strcmp(a->valuestring, b->valuestring) == 0) 3038 | { 3039 | return true; 3040 | } 3041 | 3042 | return false; 3043 | 3044 | case cJSON_Array: 3045 | { 3046 | cJSON *a_element = a->child; 3047 | cJSON *b_element = b->child; 3048 | 3049 | for (; (a_element != NULL) && (b_element != NULL);) 3050 | { 3051 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) 3052 | { 3053 | return false; 3054 | } 3055 | 3056 | a_element = a_element->next; 3057 | b_element = b_element->next; 3058 | } 3059 | 3060 | /* one of the arrays is longer than the other */ 3061 | if (a_element != b_element) { 3062 | return false; 3063 | } 3064 | 3065 | return true; 3066 | } 3067 | 3068 | case cJSON_Object: 3069 | { 3070 | cJSON *a_element = NULL; 3071 | cJSON *b_element = NULL; 3072 | cJSON_ArrayForEach(a_element, a) 3073 | { 3074 | /* TODO This has O(n^2) runtime, which is horrible! */ 3075 | b_element = get_object_item(b, a_element->string, case_sensitive); 3076 | if (b_element == NULL) 3077 | { 3078 | return false; 3079 | } 3080 | 3081 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) 3082 | { 3083 | return false; 3084 | } 3085 | } 3086 | 3087 | /* doing this twice, once on a and b to prevent true comparison if a subset of b 3088 | * TODO: Do this the proper way, this is just a fix for now */ 3089 | cJSON_ArrayForEach(b_element, b) 3090 | { 3091 | a_element = get_object_item(a, b_element->string, case_sensitive); 3092 | if (a_element == NULL) 3093 | { 3094 | return false; 3095 | } 3096 | 3097 | if (!cJSON_Compare(b_element, a_element, case_sensitive)) 3098 | { 3099 | return false; 3100 | } 3101 | } 3102 | 3103 | return true; 3104 | } 3105 | 3106 | default: 3107 | return false; 3108 | } 3109 | } 3110 | 3111 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size) 3112 | { 3113 | return global_hooks.allocate(size); 3114 | } 3115 | 3116 | CJSON_PUBLIC(void) cJSON_free(void *object) 3117 | { 3118 | global_hooks.deallocate(object); 3119 | } 3120 | --------------------------------------------------------------------------------