diff --git a/.gitignore b/.gitignore index 3167ea35..ee13d5a5 100644 --- a/.gitignore +++ b/.gitignore @@ -288,3 +288,5 @@ __pycache__/ /compile_commands.json /ApeSphere.* /.qtc_clangd + +.cache/ diff --git a/Makefile b/Makefile index fdaa6621..0ed9aa98 100644 --- a/Makefile +++ b/Makefile @@ -69,11 +69,11 @@ MACHDEP = -mno-sdata -mgcn -DGEKKO -mcpu=750 -meabi -mhard-float # -Wno-write-strings because some GC SDK functions take non-const char *, # and Ghidra can't represent const char * anyhow -CFLAGS = -nostdlib -ffreestanding -ffunction-sections -fdata-sections -g -Os -Wall -Wno-write-strings $(MACHDEP) $(INCLUDE) +CFLAGS = -nostdlib -lgcc -ffreestanding -ffunction-sections -fdata-sections -g -Os -Wall -Wno-write-strings $(MACHDEP) $(INCLUDE) CXXFLAGS = -fno-exceptions -fno-rtti -std=gnu++20 $(CFLAGS) ASFLAGS = -mregnames # Don't require % in front of register names -LDFLAGS = -r -e _prolog -u _prolog -u _epilog -u _unresolved -Wl,--gc-sections -nostdlib -g $(MACHDEP) -Wl,-Map,$(notdir $@).map +LDFLAGS = -r -e _prolog -u _prolog -u _epilog -u _unresolved -Wl,--gc-sections -nostdlib -lgcc -g $(MACHDEP) -Wl,-Map,$(notdir $@).map #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project @@ -146,7 +146,8 @@ endif export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I$(CURDIR)/$(BUILD) \ - -I$(LIBOGC_INC) + -I$(LIBOGC_INC) \ + -I$(CURDIR)/dep #--------------------------------------------------------------------------------- # build a list of library paths diff --git a/default-config.json b/default-config.json new file mode 100644 index 00000000..c9f5dbaf --- /dev/null +++ b/default-config.json @@ -0,0 +1,2502 @@ +{ + // Patches are enabled with "true" and disabled with "false" + "patches": { + // Enable specifying stage names, theme IDs, music IDs, time limits, story/challenge mode + // layouts etc. in the "story_mode_layout" and "challenge_mode_layout" sections. If you're + // making a custom level pack, you probably want to enable this. + "custom_stage_info": false, + + // When the number of bananas remaining on a stage that is marked as a bonus + // stage hits zero, the stage is completed with a 'Perfect' pop-up and voice + // line, identical to SMB1. + "perfect_bonus_completion": false, + + // Removes the heat haze on levels in theme ID 7 (sand). + // Indirectly fixes the widescreen issue with heat haze. + "remove_desert_haze": false, + + // Prevents the world music from being interrupted by the stage select music + // in Story Mode. + "story_mode_music_fix": false, + + // Prevents the music volume from being lowered when the game is paused. + "no_music_vol_decrease_on_pause": false, + + // Allows the player to select between the four playable monkeys in Story Mode. + "story_mode_char_select": false, + + // Stops the 'hurry up' music from playing. + "no_hurry_up_music": false, + + // Removes hardcoded elements from Revolution's level slot. + "fix_revolution_slot": false, + + // Fixes the lower-angle camera hardcoded to Labyrinth's level slot. + "fix_labyrinth_camera": false, + + // Challenge mode has infinite lives, and the life counter is converted into + // a counter that tracks the number of deaths. + "challenge_mode_death_count": false, + + // Disables the tutorial/ranking sequence that shows up when the player goes + // idle at the title screen. + "disable_how_to_play_screen": false, + + // Fixes an issue where wormhole surfaces do not appear on stage IDs 130 + // through 180, and stage ID 199. + "fix_wormhole_surfaces": false, + + // Fixes an issue where goal posts would not reflect properly, and makes any + // stage object with the effect bitflag 0x4 reflect in a reflective stage object. + "fix_stage_object_reflection": false, + + // Allows for arbitrarily many mirror planes on a stage, rendering the mirror plane + // from the origin of the reflective object nearest to the player. Also applies the + // item group/collision header transformation to the mirror plane, allowing for + // reflective objects to be animated. + "enhance_reflective_surfaces": false, + + // Skips the AV logo and intro movie sequence on game start, immediately + // skipping to the title screen. + "skip_intro_movie": false, + + // Allows for the SMB1 camera to be toggled with the Z button mid-game. + "smb1_camera_toggle": false, + + // Replaces the misplaced 't' letter in the story mode data entry screen with + // the correct letter, a lowercase 'w'. + "fix_missing_w": false, + + // Skips the cutscenes in story mode. + "skip_cutscenes": false, + + // Removes the playpoint screen upon returning from story or challenge mode, + // and unlocks all party games. + "remove_playpoints": false, + + // Fixes an issue with the storm theme where rain droplet splashes would not appear + // correctly on the continue platform. + "fix_storm_continue_platform": false, + + // Fixes an issue where skipping more than two or three stages in story mode with the + // any% skip glitch would lead to the game crashing. Please note that this allows for + // up to 99 skips to be performed. + "fix_any_percent_crash": false, + + // Permanently enables or disables party games based off the Party Game Toggles + // list. + "party_game_toggle": false, + + // Allows for the stages that appear in the background of the menu, stages 3 and 201, + // to have mirrors in the background. This is experimental and may cause issues. + "enable_menu_reflections": false, + + // Stage objects (stobjs) like party balls and goaltapes may show up incorrectly + // or not at all if they reside on an itemgroup whose index is >127. This patch + // increases the limit to 256 total itemgroups. + "stobj_draw_fix": false + }, + + // Toggles which party games are accessible from the party game menu. + // Disabled party games cannot be unlocked or accessed. + // Requires the patch 'party_game_toggle' to be enabled. + "party_game_toggles": { + "race": false, + "fight": false, + "target": false, + "billiards": false, + "bowling": false, + "golf": false, + "boat": false, + "shot": false, + "dogfight": false, + "soccer": false, + "baseball": false, + "tennis": false + }, + + // Defines the stages in Story Mode. Requires the "custom_stage_info" patch to be enabled. + // You may specify less than 10 worlds, but you must currently have exactly 10 stages per world. + "story_mode_layout": [ + [ // World 1 + { // Stage 1-1 + "stage_id": 201, + "name": "SIMPLE", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 1 + }, + { // Stage 1-2 + "stage_id": 202, + "name": "HOLLOW", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 2 + }, + { // Stage 1-3 + "stage_id": 203, + "name": "BUMPY", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 1 + }, + { // Stage 1-4 + "stage_id": 204, + "name": "SWITCHES", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 2 + }, + { // Stage 1-5 + "stage_id": 1, + "name": "CONVEYERS", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 1-6 + "stage_id": 2, + "name": "FLOATERS", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 1-7 + "stage_id": 3, + "name": "SLOPES", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 2 + }, + { // Stage 1-8 + "stage_id": 4, + "name": "SLIDERS", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 1-9 + "stage_id": 5, + "name": "SPINNING TOP", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 1-10 + "stage_id": 6, + "name": "CURVE BRIDGE", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "difficulty": 2 + } + ], + [ // World 2 + { // Stage 2-1 + "stage_id": 7, + "name": "BANKS", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 2 + }, + { // Stage 2-2 + "stage_id": 8, + "name": "EATEN FLOOR", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 5 + }, + { // Stage 2-3 + "stage_id": 9, + "name": "HOPPERS", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 2-4 + "stage_id": 10, + "name": "COASTER", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 2-5 + "stage_id": 11, + "name": "BUMPY CHECK", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 7 + }, + { // Stage 2-6 + "stage_id": 12, + "name": "SWELL", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 2-7 + "stage_id": 13, + "name": "GRAVITY SLIDER", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 7 + }, + { // Stage 2-8 + "stage_id": 14, + "name": "INCHWORMS", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 2-9 + "stage_id": 15, + "name": "TOTALITARIANISM", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 2-10 + "stage_id": 16, + "name": "ALTERNATIVE", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "difficulty": 6 + } + ], + [ // World 3 + { // Stage 3-1 + "stage_id": 231, + "name": "ORGANIC FORM", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 3-2 + "stage_id": 232, + "name": "REVERSIBLE GEAR", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 3-3 + "stage_id": 233, + "name": "STEPPING STONES", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 3-4 + "stage_id": 234, + "name": "DRIBBLES", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 3-5 + "stage_id": 235, + "name": "U.R.L.", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 4 + }, + { // Stage 3-6 + "stage_id": 236, + "name": "MAD RINGS", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 3-7 + "stage_id": 237, + "name": "CURVY OPTIONS", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 3-8 + "stage_id": 238, + "name": "TWISTER", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 3-9 + "stage_id": 239, + "name": "DOWNHILL", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 3-10 + "stage_id": 17, + "name": "JUNCTION", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "difficulty": 3 + } + ], + [ // World 4 + { // Stage 4-1 + "stage_id": 18, + "name": "PRO SKATERS", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 2 + }, + { // Stage 4-2 + "stage_id": 19, + "name": "GIANT COMB", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 4-3 + "stage_id": 20, + "name": "BEEHIVE", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 5 + }, + { // Stage 4-4 + "stage_id": 21, + "name": "DYNAMIC MAZE", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 1 + }, + { // Stage 4-5 + "stage_id": 22, + "name": "TRIANGLE HOLES", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 4-6 + "stage_id": 23, + "name": "LAUNCHERS", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 4-7 + "stage_id": 24, + "name": "RANDOMIZER", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 4-8 + "stage_id": 25, + "name": "COIN SLOTS", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 4-9 + "stage_id": 26, + "name": "SEESAW BRIDGES", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 4-10 + "stage_id": 27, + "name": "ARTHROPOD", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "difficulty": 10 + } + ], + [ // World 5 + { // Stage 5-1 + "stage_id": 28, + "name": "WORMHOLE", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 5-2 + "stage_id": 29, + "name": "FREE FALL", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 7 + }, + { // Stage 5-3 + "stage_id": 30, + "name": "MELTING POT", + "theme_id": 20, + "music_id": 61, + "time_limit": 30.00, + "difficulty": 4 + }, + { // Stage 5-4 + "stage_id": 31, + "name": "MAD SHUFFLE", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 5-5 + "stage_id": 32, + "name": "BEAD SCREEN", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 3 + }, + { // Stage 5-6 + "stage_id": 33, + "name": "JUMP MACHINE", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 5 + }, + { // Stage 5-7 + "stage_id": 34, + "name": "ZIGZAG SLOPE", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 2 + }, + { // Stage 5-8 + "stage_id": 35, + "name": "TOWER", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 5-9 + "stage_id": 36, + "name": "TOGGLE", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 10 + }, + { // Stage 5-10 + "stage_id": 37, + "name": "FLUCTUATION", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "difficulty": 4 + } + ], + [ // World 6 + { // Stage 6-1 + "stage_id": 38, + "name": "COMBINATION", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 6-2 + "stage_id": 39, + "name": "PUNCHED SEESAWS", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 6-3 + "stage_id": 40, + "name": "OPERA", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 5 + }, + { // Stage 6-4 + "stage_id": 41, + "name": "BRANDISHED", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 5 + }, + { // Stage 6-5 + "stage_id": 42, + "name": "TIERS", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 5 + }, + { // Stage 6-6 + "stage_id": 43, + "name": "CLIFFS", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 6-7 + "stage_id": 44, + "name": "NARROW PEAKS", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 6-8 + "stage_id": 45, + "name": "DETOUR", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 5 + }, + { // Stage 6-9 + "stage_id": 46, + "name": "SWITCH INFERNO", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 6-10 + "stage_id": 47, + "name": "FOLDERS", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "difficulty": 2 + } + ], + [ // World 7 + { // Stage 7-1 + "stage_id": 281, + "name": "SPIRAL BRIDGE", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 4 + }, + { // Stage 7-2 + "stage_id": 282, + "name": "WAVY OPTION", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 5 + }, + { // Stage 7-3 + "stage_id": 283, + "name": "OBSTACLE", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 7 + }, + { // Stage 7-4 + "stage_id": 284, + "name": "DOMINO", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 7-5 + "stage_id": 285, + "name": "SIEVE", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 4 + }, + { // Stage 7-6 + "stage_id": 286, + "name": "FLOCK", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 7-7 + "stage_id": 287, + "name": "DOUBLE SPIRAL", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 7-8 + "stage_id": 288, + "name": "HIERARCHY", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 7-9 + "stage_id": 289, + "name": "8 BRACELETS", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 10 + }, + { // Stage 7-10 + "stage_id": 48, + "name": "QUICK TURN", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "difficulty": 5 + } + ], + [ // World 8 + { // Stage 8-1 + "stage_id": 49, + "name": "PISTONS", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 8-2 + "stage_id": 50, + "name": "SOFT CREAM", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 8-3 + "stage_id": 51, + "name": "MOMENTUM", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 10 + }, + { // Stage 8-4 + "stage_id": 52, + "name": "ENTANGLED PATH", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 8-5 + "stage_id": 53, + "name": "TOTTERS", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 8-6 + "stage_id": 54, + "name": "VORTEX", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 8-7 + "stage_id": 55, + "name": "WARP", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 10 + }, + { // Stage 8-8 + "stage_id": 56, + "name": "TRAMPOLINES", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 10 + }, + { // Stage 8-9 + "stage_id": 57, + "name": "SWING SHAFT", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 8-10 + "stage_id": 58, + "name": "LINEAR SEESAWS", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "difficulty": 7 + } + ], + [ // World 9 + { // Stage 9-1 + "stage_id": 59, + "name": "SERIAL JUMP", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 10 + }, + { // Stage 9-2 + "stage_id": 60, + "name": "CROSS FLOORS", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 10 + }, + { // Stage 9-3 + "stage_id": 61, + "name": "SPINNING SAW", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 9-4 + "stage_id": 62, + "name": "CHIPPED PIPES", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 9-5 + "stage_id": 63, + "name": "FLAT MAZE", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 10 + }, + { // Stage 9-6 + "stage_id": 64, + "name": "GUILLOTINE", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 7 + }, + { // Stage 9-7 + "stage_id": 65, + "name": "CORK SCREW", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 9-8 + "stage_id": 66, + "name": "ORBITERS", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 9-9 + "stage_id": 67, + "name": "TWIN BASIN", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 6 + }, + { // Stage 9-10 + "stage_id": 68, + "name": "AIR HOCKEY", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "difficulty": 10 + } + ], + [ // World 10 + { // Stage 10-1 + "stage_id": 341, + "name": "TRAINING", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 10-2 + "stage_id": 342, + "name": "GIMMICK", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 10-3 + "stage_id": 343, + "name": "MOUNTAIN", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 10-4 + "stage_id": 344, + "name": "DISORDER", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 10-5 + "stage_id": 345, + "name": "3D MAZE", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 10-6 + "stage_id": 346, + "name": "LABYRINTH", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 10-7 + "stage_id": 347, + "name": "POSTMODERN", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 7 + }, + { // Stage 10-8 + "stage_id": 348, + "name": "REVOLUTION", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 8 + }, + { // Stage 10-9 + "stage_id": 349, + "name": "INVISIBLE", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 9 + }, + { // Stage 10-10 + "stage_id": 350, + "name": "CREATED BY", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "difficulty": 6 + } + ] + ], + + // Defines the stages in Challenge Mode. Requires the "custom_stage_info" patch to be enabled. + "challenge_mode_layout": { + "beginner": [ + { // Beginner 1 + "stage_id": 201, + "name": "SIMPLE", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner 2 + "stage_id": 202, + "name": "HOLLOW", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner 3 + "stage_id": 203, + "name": "BUMPY", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner 4 + "stage_id": 204, + "name": "SWITCHES", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner 5 + "stage_id": 205, + "name": "BOWL", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Beginner 6 + "stage_id": 206, + "name": "FLOATERS", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner 7 + "stage_id": 207, + "name": "SLOPES", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner 8 + "stage_id": 208, + "name": "SLIDERS", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner 9 + "stage_id": 209, + "name": "SPINNING TOP", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner 10 + "stage_id": 210, + "name": "CURVE BRIDGE", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + } + ], + "beginner_extra": [ + { // Beginner Extra 1 + "stage_id": 211, + "name": "CONVEYERS", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 2 + "stage_id": 212, + "name": "BUMPY CHECK", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 3 + "stage_id": 213, + "name": "ALTERNATIVE", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 4 + "stage_id": 214, + "name": "JUNCTION", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 5 + "stage_id": 215, + "name": "BEAD SCREEN", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 6 + "stage_id": 216, + "name": "FLUCTUATION", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 7 + "stage_id": 217, + "name": "FOLDERS", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 8 + "stage_id": 218, + "name": "QUICK TURN", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 9 + "stage_id": 219, + "name": "LINEAR SEESAWS", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Beginner Extra 10 + "stage_id": 220, + "name": "BIRTH", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + } + ], + "advanced": [ + { // Advanced 1 + "stage_id": 221, + "name": "BANKS", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 2 + "stage_id": 222, + "name": "EATEN FLOOR", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 3 + "stage_id": 223, + "name": "HOPPERS", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 4 + "stage_id": 224, + "name": "COASTER", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 5 + "stage_id": 225, + "name": "BOARD PARK", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Advanced 6 + "stage_id": 226, + "name": "SWELL", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 7 + "stage_id": 227, + "name": "GRAVITY SLIDER", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 8 + "stage_id": 228, + "name": "INCHWORMS", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Advanced 9 + "stage_id": 229, + "name": "TOTALITARIANISM", + "theme_id": 20, + "music_id": 61, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 10 + "stage_id": 230, + "name": "LEVELER", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Advanced 11 + "stage_id": 231, + "name": "ORGANIC FORM", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 12 + "stage_id": 232, + "name": "REVERSIBLE GEAR", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 13 + "stage_id": 233, + "name": "STEPPING STONES", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 14 + "stage_id": 234, + "name": "DRIBBLES", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 15 + "stage_id": 235, + "name": "U.R.L.", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 16 + "stage_id": 236, + "name": "MAD RINGS", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Advanced 17 + "stage_id": 237, + "name": "CURVY OPTIONS", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 18 + "stage_id": 238, + "name": "TWISTER", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 19 + "stage_id": 239, + "name": "DOWNHILL", + "theme_id": 18, + "music_id": 4, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 20 + "stage_id": 240, + "name": "RAMPAGE", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Advanced 21 + "stage_id": 241, + "name": "PRO SKATERS", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 22 + "stage_id": 242, + "name": "GIANT COMB", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 23 + "stage_id": 243, + "name": "BEEHIVE", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 24 + "stage_id": 244, + "name": "DYNAMIC MAZE", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 25 + "stage_id": 245, + "name": "TRIANGLE HOLES", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 26 + "stage_id": 246, + "name": "LAUNCHERS", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 27 + "stage_id": 247, + "name": "RANDOMIZER", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Advanced 28 + "stage_id": 248, + "name": "COIN SLOTS", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 29 + "stage_id": 249, + "name": "SEESAW BRIDGES", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced 30 + "stage_id": 250, + "name": "ARTHROPOD", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + } + ], + "advanced_extra": [ + { // Advanced Extra 1 + "stage_id": 251, + "name": "AUTO DOORS", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 2 + "stage_id": 252, + "name": "HEAVY SPHERE", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 3 + "stage_id": 253, + "name": "STAGGER", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 4 + "stage_id": 254, + "name": "U.F.O.", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 5 + "stage_id": 255, + "name": "RING BRIDGES", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 6 + "stage_id": 256, + "name": "DOMES", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 7 + "stage_id": 257, + "name": "AMIDA LOT", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 8 + "stage_id": 258, + "name": "LONG SLIDER", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 9 + "stage_id": 259, + "name": "GRID BRIDGE", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Advanced Extra 10 + "stage_id": 260, + "name": "TEAPOT", + "theme_id": 23, + "music_id": 58, + "time_limit": 30.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + } + ], + "expert": [ + { // Expert 1 + "stage_id": 261, + "name": "WORMHOLE", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 2 + "stage_id": 262, + "name": "FREE FALL", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 3 + "stage_id": 263, + "name": "MELTING POT", + "theme_id": 19, + "music_id": 7, + "time_limit": 30.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 4 + "stage_id": 264, + "name": "MAD SHUFFLE", + "theme_id": 19, + "music_id": 7, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 5 + "stage_id": 265, + "name": "PARTITION", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Expert 6 + "stage_id": 266, + "name": "JUMP MACHINE", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Expert 7 + "stage_id": 267, + "name": "ZIGZAG SLOPE", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 8 + "stage_id": 268, + "name": "TOWER", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 9 + "stage_id": 269, + "name": "TOGGLE", + "theme_id": 21, + "music_id": 6, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 10 + "stage_id": 270, + "name": "PACHINKO", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Expert 11 + "stage_id": 271, + "name": "COMBINATION", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 12 + "stage_id": 272, + "name": "PUNCHED SEESAWS", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 13 + "stage_id": 273, + "name": "OPERA", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 14 + "stage_id": 274, + "name": "BRANDISHED", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 15 + "stage_id": 275, + "name": "TIERS", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Expert 16 + "stage_id": 276, + "name": "CLIFFS", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 17 + "stage_id": 277, + "name": "NARROW PEAKS", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 18 + "stage_id": 278, + "name": "DETOUR", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 19 + "stage_id": 279, + "name": "SWITCH INFERNO", + "theme_id": 24, + "music_id": 70, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 20 + "stage_id": 280, + "name": "EARTHQUAKE", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Expert 21 + "stage_id": 281, + "name": "SPIRAL BRIDGE", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 22 + "stage_id": 282, + "name": "WAVY OPTION", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 23 + "stage_id": 283, + "name": "OBSTACLE", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 24 + "stage_id": 284, + "name": "DOMINO", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 25 + "stage_id": 285, + "name": "SIEVE", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 26 + "stage_id": 286, + "name": "FLOCK", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 27 + "stage_id": 287, + "name": "DOUBLE SPIRAL", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 28 + "stage_id": 288, + "name": "HIERARCHY", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 29 + "stage_id": 289, + "name": "8 BRACELETS", + "theme_id": 25, + "music_id": 63, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Expert 30 + "stage_id": 290, + "name": "BANANA HUNTING", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Expert 31 + "stage_id": 291, + "name": "PISTONS", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 32 + "stage_id": 292, + "name": "SOFT CREAM", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 33 + "stage_id": 293, + "name": "MOMENTUM", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 34 + "stage_id": 294, + "name": "ENTANGLED PATH", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 35 + "stage_id": 295, + "name": "TOTTERS", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 36 + "stage_id": 296, + "name": "VORTEX", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 37 + "stage_id": 297, + "name": "WARP", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 38 + "stage_id": 298, + "name": "TRAMPOLINES", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 39 + "stage_id": 299, + "name": "SWING SHAFT", + "theme_id": 17, + "music_id": 1, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 40 + "stage_id": 300, + "name": "FIGHTERS", + "theme_id": 26, + "music_id": 42, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": true + }, + { // Expert 41 + "stage_id": 301, + "name": "SERIAL JUMP", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Expert 42 + "stage_id": 302, + "name": "CROSS FLOORS", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 43 + "stage_id": 303, + "name": "SPINNING SAW", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 44 + "stage_id": 304, + "name": "CHIPPED PIPES", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 45 + "stage_id": 305, + "name": "FLAT MAZE", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 46 + "stage_id": 306, + "name": "GUILLOTINE", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 47 + "stage_id": 307, + "name": "CORK SCREW", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 48 + "stage_id": 308, + "name": "ORBITERS", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 49 + "stage_id": 309, + "name": "TWIN BASIN", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert 50 + "stage_id": 310, + "name": "AIR HOCKEY", + "theme_id": 32, + "music_id": 64, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + } + ], + "expert_extra": [ + { // Expert Extra 1 + "stage_id": 311, + "name": "CHARGE", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert Extra 2 + "stage_id": 312, + "name": "STRATA", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert Extra 3 + "stage_id": 313, + "name": "PUZZLE", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert Extra 4 + "stage_id": 314, + "name": "GIANT SWING", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert Extra 5 + "stage_id": 315, + "name": "5 DRUMS", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert Extra 6 + "stage_id": 316, + "name": "FREE THROW", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Expert Extra 7 + "stage_id": 317, + "name": "PENDULUMS", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert Extra 8 + "stage_id": 318, + "name": "CONICAL SLIDER", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert Extra 9 + "stage_id": 319, + "name": "CONSTRUCTION", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Expert Extra 10 + "stage_id": 320, + "name": "TRAIN WORM", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + } + ], + "master": [ + { // Master 1 + "stage_id": 321, + "name": "CENTRIFUGAL", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 2 + "stage_id": 322, + "name": "SWING BRIDGES", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 3 + "stage_id": 323, + "name": "CYLINDERS", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 4 + "stage_id": 324, + "name": "PASSAGE", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 5 + "stage_id": 325, + "name": "NOTCH", + "theme_id": 22, + "music_id": 16, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 6 + "stage_id": 326, + "name": "INTERMITTENT", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 7 + "stage_id": 327, + "name": "LONG TORUS", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 8 + "stage_id": 328, + "name": "SPASMODIC", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 9 + "stage_id": 329, + "name": "DOUBLE TWIN", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master 10 + "stage_id": 330, + "name": "CLOCK FACE", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + } + ], + "master_extra": [ + { // Master Extra 1 + "stage_id": 331, + "name": "VARIABLE WIDTH", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master Extra 2 + "stage_id": 332, + "name": "STRIKER", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Master Extra 3 + "stage_id": 333, + "name": "OOPARTS", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master Extra 4 + "stage_id": 334, + "name": "PLANETS", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 3, + "is_bonus_stage": false + }, + { // Master Extra 5 + "stage_id": 335, + "name": "SLICED CHEESE", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master Extra 6 + "stage_id": 336, + "name": "8 SEESAWS", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master Extra 7 + "stage_id": 337, + "name": "SYNCHRONIZED", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master Extra 8 + "stage_id": 338, + "name": "HELIX", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 2, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master Extra 9 + "stage_id": 339, + "name": "DIZZY SYSTEM", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + }, + { // Master Extra 10 + "stage_id": 340, + "name": "NINTENDO", + "theme_id": 23, + "music_id": 58, + "time_limit": 60.00, + "blue_goal_jump": 1, + "green_goal_jump": 1, + "red_goal_jump": 1, + "is_bonus_stage": false + } + ] + } +} diff --git a/default-config.txt b/default-config.txt deleted file mode 100644 index 133ba32b..00000000 --- a/default-config.txt +++ /dev/null @@ -1,1061 +0,0 @@ -// This is the config file for Workshop Mod 0.3.0! -// Place this in the root of your game directory, and name it config.txt -// It should be in the same folder as the included REL file, mkb2.rel_sample.rel -// -// DO NOT ADD NEW LINES OR ENTRIES TO THIS FILE, ONLY MODIFY EXISTING ENTRIES! -// IF YOU ABSOLUTELY MUST ADD NEW ENTRIES, USE TABS (\T) AND NOT SPACES! - -// ---------------------------------------------------------------------------- - -// Valid values: 'enabled', 'disabled', decimal integer values - -// ---------------------------------------------------------------------------- - -// Information about REL patches: -// -// perfect-bonus-completion -// -// When the number of bananas remaining on a stage that is marked as a bonus -// stage hits zero, the stage is completed with a 'Perfect' pop-up and voice -// line, identical to SMB1. - -// remove-desert-haze -// -// Removes the heat haze on levels in theme ID 7 (sand). -// Indirectly fixes the widescreen issue with heat haze. - -// story-mode-music-fix -// -// Prevents the world music from being interrupted by the stage select music -// in Story Mode. - -// no-music-vol-decrease-on-pause -// -// Prevents the music volume from being lowered when the game is paused. - -// story-mode-char-select -// -// Allows the player to select between the four playable monkeys in Story Mode. - -// no-hurry-up-music -// -// Stops the 'hurry up' music from playing. - -// fix-revolution-slot -// -// Removes hardcoded elements from Revolution's level slot. - -// fix-labyrinth-camera -// -// Fixes the lower-angle camera hardcoded to Labyrinth's level slot. - -// challenge-mode-death-count -// -// Challenge mode has infinite lives, and the life counter is converted into -// a counter that tracks the number of deaths. - -// disable-how-to-play-screen -// -// Disables the tutorial/ranking sequence that shows up when the player goes -// idle at the title screen. - -// fix-wormhole-surfaces -// -// Fixes an issue where wormhole surfaces do not appear on stage IDs 130 -// through 180, and stage ID 199. - -// fix-stage-object-reflection -// -// Fixes an issue where goal posts would not reflect properly, and makes any -// stage object with the effect bitflag 0x4 reflect in a reflective stage object. - -// enhance-reflective-surfaces -// -// Allows for arbitrarily many mirror planes on a stage, rendering the mirror plane -// from the origin of the reflective object nearest to the player. Also applies the -// item group/collision header transformation to the mirror plane, allowing for -// reflective objects to be animated. - -// custom-music-id -// -// Draws music ID data from a per-stage list, defined below, instead of basing -// it on the active theme. - -// custom-theme-id -// -// Draws theme ID data from a per-stage list, defined below, instead of using -// the built-in list. - -// skip-intro-movie -// -// Skips the AV logo and intro movie sequence on game start, immediately -// skipping to the title screen. - -// smb1-camera-toggle -// -// Allows for the SMB1 camera to be toggled with the Z button mid-game. - -// fix-missing-w -// -// Replaces the misplaced 't' letter in the story mode data entry screen with -// the correct letter, a lowercase 'w'. - -// skip-cutscenes -// -// Skips the cutscenes in story mode. - -// remove-playpoints -// -// Removes the playpoint screen upon returning from story or challenge mode, -// and unlocks all party games. - -// fix-storm-continue-platform -// -// Fixes an issue with the storm theme where rain droplet splashes would not appear -// correctly on the continue platform. - -// fix-any-percent-crash -// -// Fixes an issue where skipping more than two or three stages in story mode with the -// any% skip glitch would lead to the game crashing. Please note that this allows for -// up to 99 skips to be performed. - -// party-game-toggle -// -// Permanently enables or disables party games based off the Party Game Toggles -// list, defined below. - -// enable-menu-reflections -// -// Allows for the stages that appear in the background of the menu, stages 3 and 201, -// to have mirrors in the background. This is experimental and may cause issues. - -// custom-world-count -// -// Allows for the number of worlds in story mode to be modified. After the final world, -// the end cutscene will play, or the credits sequence will start if the cutscene skip -// patch is enabled. The minimum value is 1, and the maximum value is 10. - -// stobj-draw-fix -// -// Stage objects (stobjs) like party balls and goaltapes may show up incorrectly -// or not at all if they reside on an itemgroup whose index is >127. This patch -// increases the limit to 256 total itemgroups. - -// ---------------------------------------------------------------------------- - -// 'enabled' - Applies the patch -// 'disabled' - Does not apply the patch - -# REL Patches { - perfect-bonus-completion: disabled - remove-desert-haze: disabled - story-mode-music-fix: disabled - no-music-vol-decrease-on-pause: disabled - story-mode-char-select: disabled - no-hurry-up-music: disabled - fix-revolution-slot: disabled - fix-labyrinth-camera: disabled - challenge-mode-death-count: disabled - disable-how-to-play-screen: disabled - fix-wormhole-surfaces: disabled - fix-stage-object-reflection: disabled - enhance-reflective-surfaces: disabled - custom-music-id: disabled - custom-theme-id: disabled - skip-intro-movie: disabled - smb1-camera-toggle: disabled - fix-missing-w: disabled - skip-cutscenes: disabled - remove-playpoints: disabled - fix-storm-continue-platform: disabled - fix-any-percent-crash: disabled - party-game-toggle: disabled - enable-menu-reflections: disabled - custom-world-count: 10 - stobj-draw-fix: disabled -} - -// Toggles which party games are accessible from the party game menu. -// Party games marked with 'disabled' will be permanently disabled, and -// cannot be unlocked or accessed. -// Requires the patch 'party-game-toggle' to be enabled. - -# Party Game Toggles { - monkey-race: disabled - monkey-fight: disabled - monkey-target: disabled - monkey-billiards: disabled - monkey-bowling: disabled - monkey-golf: disabled - monkey-boat: disabled - monkey-shot: disabled - monkey-dogfight: disabled - monkey-soccer: disabled - monkey-baseball: disabled - monkey-tennis: disabled -} - -// Stage ID to theme ID mapping. Theme IDs are in decimal. -// The default, vanilla theme ID list is provided for your convenience. -// A theme ID list is included with the download of this mod. -// Requires the patch 'custom-theme-id' to be enabled. - -# Theme IDs { - STAGE 0: 0 - STAGE 1: 19 - STAGE 2: 19 - STAGE 3: 19 - STAGE 4: 19 - STAGE 5: 19 - STAGE 6: 19 - STAGE 7: 17 - STAGE 8: 17 - STAGE 9: 17 - STAGE 10: 17 - STAGE 11: 17 - STAGE 12: 17 - STAGE 13: 17 - STAGE 14: 17 - STAGE 15: 17 - STAGE 16: 17 - STAGE 17: 18 - STAGE 18: 32 - STAGE 19: 32 - STAGE 20: 32 - STAGE 21: 32 - STAGE 22: 32 - STAGE 23: 32 - STAGE 24: 32 - STAGE 25: 32 - STAGE 26: 32 - STAGE 27: 32 - STAGE 28: 20 - STAGE 29: 20 - STAGE 30: 20 - STAGE 31: 20 - STAGE 32: 20 - STAGE 33: 20 - STAGE 34: 20 - STAGE 35: 20 - STAGE 36: 20 - STAGE 37: 20 - STAGE 38: 21 - STAGE 39: 21 - STAGE 40: 21 - STAGE 41: 21 - STAGE 42: 21 - STAGE 43: 21 - STAGE 44: 21 - STAGE 45: 21 - STAGE 46: 21 - STAGE 47: 21 - STAGE 48: 25 - STAGE 49: 24 - STAGE 50: 24 - STAGE 51: 24 - STAGE 52: 24 - STAGE 53: 24 - STAGE 54: 24 - STAGE 55: 24 - STAGE 56: 24 - STAGE 57: 24 - STAGE 58: 24 - STAGE 59: 22 - STAGE 60: 22 - STAGE 61: 22 - STAGE 62: 22 - STAGE 63: 22 - STAGE 64: 22 - STAGE 65: 22 - STAGE 66: 22 - STAGE 67: 22 - STAGE 68: 22 - STAGE 69: 1 - STAGE 70: 19 - STAGE 71: 1 - STAGE 72: 1 - STAGE 73: 1 - STAGE 74: 1 - STAGE 75: 1 - STAGE 76: 1 - STAGE 77: 1 - STAGE 78: 1 - STAGE 79: 1 - STAGE 80: 1 - STAGE 81: 1 - STAGE 82: 1 - STAGE 83: 1 - STAGE 84: 1 - STAGE 85: 1 - STAGE 86: 1 - STAGE 87: 1 - STAGE 88: 1 - STAGE 89: 1 - STAGE 90: 1 - STAGE 91: 1 - STAGE 92: 1 - STAGE 93: 1 - STAGE 94: 1 - STAGE 95: 1 - STAGE 96: 1 - STAGE 97: 1 - STAGE 98: 1 - STAGE 99: 1 - STAGE 100: 1 - STAGE 101: 27 - STAGE 102: 27 - STAGE 103: 1 - STAGE 104: 1 - STAGE 105: 1 - STAGE 106: 1 - STAGE 107: 1 - STAGE 108: 1 - STAGE 109: 1 - STAGE 110: 1 - STAGE 111: 1 - STAGE 112: 1 - STAGE 113: 1 - STAGE 114: 1 - STAGE 115: 1 - STAGE 116: 1 - STAGE 117: 1 - STAGE 118: 1 - STAGE 119: 1 - STAGE 120: 1 - STAGE 121: 19 - STAGE 122: 18 - STAGE 123: 22 - STAGE 124: 1 - STAGE 125: 1 - STAGE 126: 1 - STAGE 127: 1 - STAGE 128: 1 - STAGE 129: 1 - STAGE 130: 1 - STAGE 131: 19 - STAGE 132: 20 - STAGE 133: 24 - STAGE 134: 17 - STAGE 135: 22 - STAGE 136: 18 - STAGE 137: 4 - STAGE 138: 31 - STAGE 139: 31 - STAGE 140: 31 - STAGE 141: 6 - STAGE 142: 3 - STAGE 143: 19 - STAGE 144: 20 - STAGE 145: 34 - STAGE 146: 20 - STAGE 147: 21 - STAGE 148: 6 - STAGE 149: 6 - STAGE 150: 19 - STAGE 151: 11 - STAGE 152: 29 - STAGE 153: 26 - STAGE 154: 23 - STAGE 155: 29 - STAGE 156: 29 - STAGE 157: 29 - STAGE 158: 29 - STAGE 159: 28 - STAGE 160: 30 - STAGE 161: 33 - STAGE 162: 33 - STAGE 163: 33 - STAGE 164: 33 - STAGE 165: 33 - STAGE 166: 33 - STAGE 167: 33 - STAGE 168: 33 - STAGE 169: 33 - STAGE 170: 33 - STAGE 171: 33 - STAGE 172: 33 - STAGE 173: 33 - STAGE 174: 33 - STAGE 175: 33 - STAGE 176: 33 - STAGE 177: 33 - STAGE 178: 33 - STAGE 179: 14 - STAGE 180: 12 - STAGE 181: 3 - STAGE 182: 3 - STAGE 183: 3 - STAGE 184: 1 - STAGE 185: 1 - STAGE 186: 1 - STAGE 187: 1 - STAGE 188: 1 - STAGE 189: 1 - STAGE 190: 1 - STAGE 191: 1 - STAGE 192: 1 - STAGE 193: 1 - STAGE 194: 1 - STAGE 195: 19 - STAGE 196: 1 - STAGE 197: 10 - STAGE 198: 16 - STAGE 199: 2 - STAGE 200: 20 - STAGE 201: 19 - STAGE 202: 19 - STAGE 203: 19 - STAGE 204: 19 - STAGE 205: 26 - STAGE 206: 20 - STAGE 207: 20 - STAGE 208: 20 - STAGE 209: 20 - STAGE 210: 20 - STAGE 211: 22 - STAGE 212: 22 - STAGE 213: 22 - STAGE 214: 22 - STAGE 215: 22 - STAGE 216: 23 - STAGE 217: 23 - STAGE 218: 23 - STAGE 219: 23 - STAGE 220: 23 - STAGE 221: 19 - STAGE 222: 19 - STAGE 223: 19 - STAGE 224: 19 - STAGE 225: 26 - STAGE 226: 20 - STAGE 227: 20 - STAGE 228: 20 - STAGE 229: 20 - STAGE 230: 26 - STAGE 231: 18 - STAGE 232: 18 - STAGE 233: 18 - STAGE 234: 18 - STAGE 235: 18 - STAGE 236: 18 - STAGE 237: 18 - STAGE 238: 18 - STAGE 239: 18 - STAGE 240: 26 - STAGE 241: 21 - STAGE 242: 21 - STAGE 243: 21 - STAGE 244: 21 - STAGE 245: 21 - STAGE 246: 21 - STAGE 247: 21 - STAGE 248: 21 - STAGE 249: 21 - STAGE 250: 21 - STAGE 251: 22 - STAGE 252: 22 - STAGE 253: 22 - STAGE 254: 22 - STAGE 255: 22 - STAGE 256: 23 - STAGE 257: 23 - STAGE 258: 23 - STAGE 259: 23 - STAGE 260: 23 - STAGE 261: 19 - STAGE 262: 19 - STAGE 263: 19 - STAGE 264: 19 - STAGE 265: 26 - STAGE 266: 21 - STAGE 267: 21 - STAGE 268: 21 - STAGE 269: 21 - STAGE 270: 26 - STAGE 271: 24 - STAGE 272: 24 - STAGE 273: 24 - STAGE 274: 24 - STAGE 275: 24 - STAGE 276: 24 - STAGE 277: 24 - STAGE 278: 24 - STAGE 279: 24 - STAGE 280: 26 - STAGE 281: 25 - STAGE 282: 25 - STAGE 283: 25 - STAGE 284: 25 - STAGE 285: 25 - STAGE 286: 25 - STAGE 287: 25 - STAGE 288: 25 - STAGE 289: 25 - STAGE 290: 26 - STAGE 291: 17 - STAGE 292: 17 - STAGE 293: 17 - STAGE 294: 17 - STAGE 295: 17 - STAGE 296: 17 - STAGE 297: 17 - STAGE 298: 17 - STAGE 299: 17 - STAGE 300: 26 - STAGE 301: 32 - STAGE 302: 32 - STAGE 303: 32 - STAGE 304: 32 - STAGE 305: 32 - STAGE 306: 32 - STAGE 307: 32 - STAGE 308: 32 - STAGE 309: 32 - STAGE 310: 32 - STAGE 311: 22 - STAGE 312: 22 - STAGE 313: 22 - STAGE 314: 22 - STAGE 315: 22 - STAGE 316: 22 - STAGE 317: 22 - STAGE 318: 22 - STAGE 319: 22 - STAGE 320: 22 - STAGE 321: 22 - STAGE 322: 22 - STAGE 323: 22 - STAGE 324: 22 - STAGE 325: 22 - STAGE 326: 23 - STAGE 327: 23 - STAGE 328: 23 - STAGE 329: 23 - STAGE 330: 23 - STAGE 331: 23 - STAGE 332: 23 - STAGE 333: 23 - STAGE 334: 23 - STAGE 335: 23 - STAGE 336: 23 - STAGE 337: 23 - STAGE 338: 23 - STAGE 339: 23 - STAGE 340: 23 - STAGE 341: 23 - STAGE 342: 23 - STAGE 343: 23 - STAGE 344: 23 - STAGE 345: 23 - STAGE 346: 23 - STAGE 347: 23 - STAGE 348: 23 - STAGE 349: 23 - STAGE 350: 23 - STAGE 351: 1 - STAGE 352: 1 - STAGE 353: 1 - STAGE 354: 1 - STAGE 355: 1 - STAGE 356: 1 - STAGE 357: 1 - STAGE 358: 1 - STAGE 359: 1 - STAGE 360: 1 - STAGE 361: 1 - STAGE 362: 1 - STAGE 363: 1 - STAGE 364: 1 - STAGE 365: 1 - STAGE 366: 1 - STAGE 367: 1 - STAGE 368: 1 - STAGE 369: 1 - STAGE 370: 1 - STAGE 371: 1 - STAGE 372: 1 - STAGE 373: 1 - STAGE 374: 1 - STAGE 375: 1 - STAGE 376: 1 - STAGE 377: 1 - STAGE 378: 1 - STAGE 379: 1 - STAGE 380: 1 - STAGE 381: 1 - STAGE 382: 1 - STAGE 383: 1 - STAGE 384: 1 - STAGE 385: 1 - STAGE 386: 1 - STAGE 387: 1 - STAGE 388: 1 - STAGE 389: 1 - STAGE 390: 1 - STAGE 391: 1 - STAGE 392: 1 - STAGE 393: 1 - STAGE 394: 1 - STAGE 395: 1 - STAGE 396: 1 - STAGE 397: 29 - STAGE 398: 23 - STAGE 399: 35 - STAGE 400: 22 - STAGE 401: 29 - STAGE 402: 35 - STAGE 403: 19 - STAGE 404: 29 - STAGE 405: 17 - STAGE 406: 40 - STAGE 407: 39 - STAGE 408: 35 - STAGE 409: 32 - STAGE 410: 37 - STAGE 411: 37 - STAGE 412: 35 - STAGE 413: 38 - STAGE 414: 21 - STAGE 415: 36 - STAGE 416: 41 - STAGE 417: 22 - STAGE 418: 23 - STAGE 419: 35 - STAGE 420: 32 -} - -// To be implemented soon - -# Difficulty Layout { - -} - -// Stage ID to music ID mapping. Music IDs are in decimal. -// The default, vanilla music ID list is provided for your convenience. -// A music ID list is included with the download of this mod. -// Requires the patch 'custom-music-id' to be enabled. - -# Music IDs { - STAGE 0: 0 - STAGE 1: 7 - STAGE 2: 7 - STAGE 3: 7 - STAGE 4: 7 - STAGE 5: 7 - STAGE 6: 7 - STAGE 7: 1 - STAGE 8: 1 - STAGE 9: 1 - STAGE 10: 1 - STAGE 11: 1 - STAGE 12: 1 - STAGE 13: 1 - STAGE 14: 1 - STAGE 15: 1 - STAGE 16: 1 - STAGE 17: 4 - STAGE 18: 64 - STAGE 19: 64 - STAGE 20: 64 - STAGE 21: 64 - STAGE 22: 64 - STAGE 23: 64 - STAGE 24: 64 - STAGE 25: 64 - STAGE 26: 64 - STAGE 27: 64 - STAGE 28: 61 - STAGE 29: 61 - STAGE 30: 61 - STAGE 31: 61 - STAGE 32: 61 - STAGE 33: 61 - STAGE 34: 61 - STAGE 35: 61 - STAGE 36: 61 - STAGE 37: 61 - STAGE 38: 6 - STAGE 39: 6 - STAGE 40: 6 - STAGE 41: 6 - STAGE 42: 6 - STAGE 43: 6 - STAGE 44: 6 - STAGE 45: 6 - STAGE 46: 6 - STAGE 47: 6 - STAGE 48: 63 - STAGE 49: 70 - STAGE 50: 70 - STAGE 51: 70 - STAGE 52: 70 - STAGE 53: 70 - STAGE 54: 70 - STAGE 55: 70 - STAGE 56: 70 - STAGE 57: 70 - STAGE 58: 70 - STAGE 59: 16 - STAGE 60: 16 - STAGE 61: 16 - STAGE 62: 16 - STAGE 63: 16 - STAGE 64: 16 - STAGE 65: 16 - STAGE 66: 16 - STAGE 67: 16 - STAGE 68: 16 - STAGE 69: 0 - STAGE 70: 7 - STAGE 71: 0 - STAGE 72: 0 - STAGE 73: 0 - STAGE 74: 0 - STAGE 75: 0 - STAGE 76: 0 - STAGE 77: 0 - STAGE 78: 0 - STAGE 79: 0 - STAGE 80: 0 - STAGE 81: 0 - STAGE 82: 0 - STAGE 83: 0 - STAGE 84: 0 - STAGE 85: 0 - STAGE 86: 0 - STAGE 87: 0 - STAGE 88: 0 - STAGE 89: 0 - STAGE 90: 0 - STAGE 91: 0 - STAGE 92: 0 - STAGE 93: 0 - STAGE 94: 0 - STAGE 95: 0 - STAGE 96: 0 - STAGE 97: 0 - STAGE 98: 0 - STAGE 99: 0 - STAGE 100: 0 - STAGE 101: 0 - STAGE 102: 0 - STAGE 103: 0 - STAGE 104: 0 - STAGE 105: 0 - STAGE 106: 0 - STAGE 107: 0 - STAGE 108: 0 - STAGE 109: 0 - STAGE 110: 0 - STAGE 111: 0 - STAGE 112: 0 - STAGE 113: 0 - STAGE 114: 0 - STAGE 115: 0 - STAGE 116: 0 - STAGE 117: 0 - STAGE 118: 0 - STAGE 119: 0 - STAGE 120: 0 - STAGE 121: 7 - STAGE 122: 4 - STAGE 123: 16 - STAGE 124: 0 - STAGE 125: 0 - STAGE 126: 0 - STAGE 127: 0 - STAGE 128: 0 - STAGE 129: 0 - STAGE 130: 0 - STAGE 131: 7 - STAGE 132: 61 - STAGE 133: 70 - STAGE 134: 1 - STAGE 135: 16 - STAGE 136: 4 - STAGE 137: 0 - STAGE 138: 0 - STAGE 139: 0 - STAGE 140: 0 - STAGE 141: 0 - STAGE 142: 0 - STAGE 143: 7 - STAGE 144: 61 - STAGE 145: 0 - STAGE 146: 61 - STAGE 147: 6 - STAGE 148: 0 - STAGE 149: 0 - STAGE 150: 7 - STAGE 151: 0 - STAGE 152: 0 - STAGE 153: 42 - STAGE 154: 58 - STAGE 155: 0 - STAGE 156: 0 - STAGE 157: 0 - STAGE 158: 0 - STAGE 159: 0 - STAGE 160: 0 - STAGE 161: 0 - STAGE 162: 0 - STAGE 163: 0 - STAGE 164: 0 - STAGE 165: 0 - STAGE 166: 0 - STAGE 167: 0 - STAGE 168: 0 - STAGE 169: 0 - STAGE 170: 0 - STAGE 171: 0 - STAGE 172: 0 - STAGE 173: 0 - STAGE 174: 0 - STAGE 175: 0 - STAGE 176: 0 - STAGE 177: 0 - STAGE 178: 0 - STAGE 179: 0 - STAGE 180: 0 - STAGE 181: 0 - STAGE 182: 0 - STAGE 183: 0 - STAGE 184: 0 - STAGE 185: 0 - STAGE 186: 0 - STAGE 187: 0 - STAGE 188: 0 - STAGE 189: 0 - STAGE 190: 0 - STAGE 191: 0 - STAGE 192: 0 - STAGE 193: 0 - STAGE 194: 0 - STAGE 195: 7 - STAGE 196: 0 - STAGE 197: 0 - STAGE 198: 0 - STAGE 199: 7 - STAGE 200: 61 - STAGE 201: 7 - STAGE 202: 7 - STAGE 203: 7 - STAGE 204: 7 - STAGE 205: 42 - STAGE 206: 61 - STAGE 207: 61 - STAGE 208: 61 - STAGE 209: 61 - STAGE 210: 61 - STAGE 211: 16 - STAGE 212: 16 - STAGE 213: 16 - STAGE 214: 16 - STAGE 215: 16 - STAGE 216: 58 - STAGE 217: 58 - STAGE 218: 58 - STAGE 219: 58 - STAGE 220: 58 - STAGE 221: 7 - STAGE 222: 7 - STAGE 223: 7 - STAGE 224: 7 - STAGE 225: 42 - STAGE 226: 61 - STAGE 227: 61 - STAGE 228: 61 - STAGE 229: 61 - STAGE 230: 42 - STAGE 231: 4 - STAGE 232: 4 - STAGE 233: 4 - STAGE 234: 4 - STAGE 235: 4 - STAGE 236: 4 - STAGE 237: 4 - STAGE 238: 4 - STAGE 239: 4 - STAGE 240: 42 - STAGE 241: 6 - STAGE 242: 6 - STAGE 243: 6 - STAGE 244: 6 - STAGE 245: 6 - STAGE 246: 6 - STAGE 247: 6 - STAGE 248: 6 - STAGE 249: 6 - STAGE 250: 6 - STAGE 251: 16 - STAGE 252: 16 - STAGE 253: 16 - STAGE 254: 16 - STAGE 255: 16 - STAGE 256: 58 - STAGE 257: 58 - STAGE 258: 58 - STAGE 259: 58 - STAGE 260: 58 - STAGE 261: 7 - STAGE 262: 7 - STAGE 263: 7 - STAGE 264: 7 - STAGE 265: 42 - STAGE 266: 6 - STAGE 267: 6 - STAGE 268: 6 - STAGE 269: 6 - STAGE 270: 42 - STAGE 271: 70 - STAGE 272: 70 - STAGE 273: 70 - STAGE 274: 70 - STAGE 275: 70 - STAGE 276: 70 - STAGE 277: 70 - STAGE 278: 70 - STAGE 279: 70 - STAGE 280: 42 - STAGE 281: 63 - STAGE 282: 63 - STAGE 283: 63 - STAGE 284: 63 - STAGE 285: 63 - STAGE 286: 63 - STAGE 287: 63 - STAGE 288: 63 - STAGE 289: 63 - STAGE 290: 42 - STAGE 291: 1 - STAGE 292: 1 - STAGE 293: 1 - STAGE 294: 1 - STAGE 295: 1 - STAGE 296: 1 - STAGE 297: 1 - STAGE 298: 1 - STAGE 299: 1 - STAGE 300: 42 - STAGE 301: 64 - STAGE 302: 64 - STAGE 303: 64 - STAGE 304: 64 - STAGE 305: 64 - STAGE 306: 64 - STAGE 307: 64 - STAGE 308: 64 - STAGE 309: 64 - STAGE 310: 64 - STAGE 311: 16 - STAGE 312: 16 - STAGE 313: 16 - STAGE 314: 16 - STAGE 315: 16 - STAGE 316: 16 - STAGE 317: 16 - STAGE 318: 16 - STAGE 319: 16 - STAGE 320: 16 - STAGE 321: 16 - STAGE 322: 16 - STAGE 323: 16 - STAGE 324: 16 - STAGE 325: 16 - STAGE 326: 58 - STAGE 327: 58 - STAGE 328: 58 - STAGE 329: 58 - STAGE 330: 58 - STAGE 331: 58 - STAGE 332: 58 - STAGE 333: 58 - STAGE 334: 58 - STAGE 335: 58 - STAGE 336: 58 - STAGE 337: 58 - STAGE 338: 58 - STAGE 339: 58 - STAGE 340: 58 - STAGE 341: 58 - STAGE 342: 58 - STAGE 343: 58 - STAGE 344: 58 - STAGE 345: 58 - STAGE 346: 58 - STAGE 347: 58 - STAGE 348: 58 - STAGE 349: 58 - STAGE 350: 58 - STAGE 351: 0 - STAGE 352: 0 - STAGE 353: 0 - STAGE 354: 0 - STAGE 355: 0 - STAGE 356: 0 - STAGE 357: 0 - STAGE 358: 0 - STAGE 359: 0 - STAGE 360: 0 - STAGE 361: 0 - STAGE 362: 0 - STAGE 363: 0 - STAGE 364: 0 - STAGE 365: 0 - STAGE 366: 0 - STAGE 367: 0 - STAGE 368: 0 - STAGE 369: 0 - STAGE 370: 0 - STAGE 371: 0 - STAGE 372: 0 - STAGE 373: 0 - STAGE 374: 0 - STAGE 375: 0 - STAGE 376: 0 - STAGE 377: 0 - STAGE 378: 0 - STAGE 379: 0 - STAGE 380: 0 - STAGE 381: 0 - STAGE 382: 0 - STAGE 383: 0 - STAGE 384: 0 - STAGE 385: 0 - STAGE 386: 0 - STAGE 387: 0 - STAGE 388: 0 - STAGE 389: 0 - STAGE 390: 0 - STAGE 391: 0 - STAGE 392: 0 - STAGE 393: 0 - STAGE 394: 0 - STAGE 395: 0 - STAGE 396: 0 - STAGE 397: 0 - STAGE 398: 58 - STAGE 399: 0 - STAGE 400: 16 - STAGE 401: 0 - STAGE 402: 0 - STAGE 403: 7 - STAGE 404: 0 - STAGE 405: 1 - STAGE 406: 0 - STAGE 407: 0 - STAGE 408: 0 - STAGE 409: 64 - STAGE 410: 0 - STAGE 411: 0 - STAGE 412: 0 - STAGE 413: 0 - STAGE 414: 6 - STAGE 415: 0 - STAGE 416: 0 - STAGE 417: 16 - STAGE 418: 58 - STAGE 419: 0 - STAGE 420: 61 -} diff --git a/dep/ArduinoJson/ArduinoJson.h b/dep/ArduinoJson/ArduinoJson.h new file mode 100644 index 00000000..fb2a5365 --- /dev/null +++ b/dep/ArduinoJson/ArduinoJson.h @@ -0,0 +1,7160 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2023, Benoit BLANCHON +// MIT License + +#pragma once + +#ifdef __cplusplus + +#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_VER < 1910) +# error ArduinoJson requires C++11 or newer. Configure your compiler for C++11 or downgrade ArduinoJson to 6.20. +#endif +#ifndef ARDUINOJSON_ENABLE_STD_STREAM +# ifdef __has_include +# if __has_include() && \ + __has_include() && \ + !defined(min) && \ + !defined(max) +# define ARDUINOJSON_ENABLE_STD_STREAM 1 +# else +# define ARDUINOJSON_ENABLE_STD_STREAM 0 +# endif +# else +# ifdef ARDUINO +# define ARDUINOJSON_ENABLE_STD_STREAM 0 +# else +# define ARDUINOJSON_ENABLE_STD_STREAM 1 +# endif +# endif +#endif +#ifndef ARDUINOJSON_ENABLE_STD_STRING +# ifdef __has_include +# if __has_include() && !defined(min) && !defined(max) +# define ARDUINOJSON_ENABLE_STD_STRING 1 +# else +# define ARDUINOJSON_ENABLE_STD_STRING 0 +# endif +# else +# ifdef ARDUINO +# define ARDUINOJSON_ENABLE_STD_STRING 0 +# else +# define ARDUINOJSON_ENABLE_STD_STRING 1 +# endif +# endif +#endif +#ifndef ARDUINOJSON_ENABLE_STRING_VIEW +# ifdef __has_include +# if __has_include() && __cplusplus >= 201703L +# define ARDUINOJSON_ENABLE_STRING_VIEW 1 +# else +# define ARDUINOJSON_ENABLE_STRING_VIEW 0 +# endif +# else +# define ARDUINOJSON_ENABLE_STRING_VIEW 0 +# endif +#endif +#ifndef ARDUINOJSON_USE_DOUBLE +# define ARDUINOJSON_USE_DOUBLE 1 +#endif +#ifndef ARDUINOJSON_USE_LONG_LONG +# if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 4 || \ + defined(_MSC_VER) +# define ARDUINOJSON_USE_LONG_LONG 1 +# endif +#endif +#ifndef ARDUINOJSON_USE_LONG_LONG +# define ARDUINOJSON_USE_LONG_LONG 0 +#endif +#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT +# define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 +#endif +#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE +# if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ <= 2 +# define ARDUINOJSON_SLOT_OFFSET_SIZE 1 +# elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 8 || \ + defined(_WIN64) && _WIN64 +# define ARDUINOJSON_SLOT_OFFSET_SIZE 4 +# else +# define ARDUINOJSON_SLOT_OFFSET_SIZE 2 +# endif +#endif +#ifdef ARDUINO +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +# define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 +# endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +# define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 +# endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +# define ARDUINOJSON_ENABLE_ARDUINO_PRINT 1 +# endif +# ifndef ARDUINOJSON_ENABLE_PROGMEM +# define ARDUINOJSON_ENABLE_PROGMEM 1 +# endif +#else // ARDUINO +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +# define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 +# endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +# define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0 +# endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +# define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0 +# endif +# ifndef ARDUINOJSON_ENABLE_PROGMEM +# ifdef __AVR__ +# define ARDUINOJSON_ENABLE_PROGMEM 1 +# else +# define ARDUINOJSON_ENABLE_PROGMEM 0 +# endif +# endif +#endif // ARDUINO +#ifndef ARDUINOJSON_DECODE_UNICODE +# define ARDUINOJSON_DECODE_UNICODE 1 +#endif +#ifndef ARDUINOJSON_ENABLE_COMMENTS +# define ARDUINOJSON_ENABLE_COMMENTS 0 +#endif +#ifndef ARDUINOJSON_ENABLE_NAN +# define ARDUINOJSON_ENABLE_NAN 0 +#endif +#ifndef ARDUINOJSON_ENABLE_INFINITY +# define ARDUINOJSON_ENABLE_INFINITY 0 +#endif +#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD +# define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7 +#endif +#ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD +# define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 +#endif +#ifndef ARDUINOJSON_LITTLE_ENDIAN +# if defined(_MSC_VER) || \ + (defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__i386) || defined(__x86_64) +# define ARDUINOJSON_LITTLE_ENDIAN 1 +# else +# define ARDUINOJSON_LITTLE_ENDIAN 0 +# endif +#endif +#ifndef ARDUINOJSON_ENABLE_ALIGNMENT +# if defined(__AVR) +# define ARDUINOJSON_ENABLE_ALIGNMENT 0 +# else +# define ARDUINOJSON_ENABLE_ALIGNMENT 1 +# endif +#endif +#ifndef ARDUINOJSON_TAB +# define ARDUINOJSON_TAB " " +#endif +#ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION +# define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1 +#endif +#ifndef ARDUINOJSON_STRING_BUFFER_SIZE +# define ARDUINOJSON_STRING_BUFFER_SIZE 32 +#endif +#ifndef ARDUINOJSON_DEBUG +# ifdef __PLATFORMIO_BUILD_DEBUG__ +# define ARDUINOJSON_DEBUG 1 +# else +# define ARDUINOJSON_DEBUG 0 +# endif +#endif +#if defined(nullptr) +# error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr +#endif +#if ARDUINOJSON_ENABLE_ARDUINO_STRING || ARDUINOJSON_ENABLE_ARDUINO_STREAM || \ + ARDUINOJSON_ENABLE_ARDUINO_PRINT || \ + (ARDUINOJSON_ENABLE_PROGMEM && defined(ARDUINO)) +#include +#endif +#if !ARDUINOJSON_DEBUG +# ifdef __clang__ +# pragma clang system_header +# elif defined __GNUC__ +# pragma GCC system_header +# endif +#endif +#define ARDUINOJSON_CONCAT_(A, B) A##B +#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B) +#define ARDUINOJSON_CONCAT3(A, B, C) \ + ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), C) +#define ARDUINOJSON_CONCAT4(A, B, C, D) \ + ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D)) +#define ARDUINOJSON_BIN2ALPHA_0000() A +#define ARDUINOJSON_BIN2ALPHA_0001() B +#define ARDUINOJSON_BIN2ALPHA_0010() C +#define ARDUINOJSON_BIN2ALPHA_0011() D +#define ARDUINOJSON_BIN2ALPHA_0100() E +#define ARDUINOJSON_BIN2ALPHA_0101() F +#define ARDUINOJSON_BIN2ALPHA_0110() F +#define ARDUINOJSON_BIN2ALPHA_0111() H +#define ARDUINOJSON_BIN2ALPHA_1000() I +#define ARDUINOJSON_BIN2ALPHA_1001() J +#define ARDUINOJSON_BIN2ALPHA_1010() K +#define ARDUINOJSON_BIN2ALPHA_1011() L +#define ARDUINOJSON_BIN2ALPHA_1100() M +#define ARDUINOJSON_BIN2ALPHA_1101() N +#define ARDUINOJSON_BIN2ALPHA_1110() O +#define ARDUINOJSON_BIN2ALPHA_1111() P +#define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D() +#define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D) +#define ARDUINOJSON_VERSION "6.21.2" +#define ARDUINOJSON_VERSION_MAJOR 6 +#define ARDUINOJSON_VERSION_MINOR 21 +#define ARDUINOJSON_VERSION_REVISION 2 +#ifndef ARDUINOJSON_VERSION_NAMESPACE +# define ARDUINOJSON_VERSION_NAMESPACE \ + ARDUINOJSON_CONCAT3( \ + ARDUINOJSON_CONCAT4(V, ARDUINOJSON_VERSION_MAJOR, \ + ARDUINOJSON_VERSION_MINOR, \ + ARDUINOJSON_VERSION_REVISION), \ + ARDUINOJSON_BIN2ALPHA( \ + ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG, \ + ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \ + ARDUINOJSON_BIN2ALPHA( \ + ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \ + ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE)) +#endif +#define ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE \ + namespace ArduinoJson { \ + inline namespace ARDUINOJSON_VERSION_NAMESPACE { +#define ARDUINOJSON_END_PUBLIC_NAMESPACE \ + } \ + } +#define ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE \ + namespace ArduinoJson { \ + inline namespace ARDUINOJSON_VERSION_NAMESPACE { \ + namespace detail { +#define ARDUINOJSON_END_PRIVATE_NAMESPACE \ + } \ + } \ + } +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +struct Converter; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class InvalidConversion; // Error here? See https://arduinojson.org/v6/invalid-conversion/ +template +struct ConverterNeedsWriteableRef; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#include +#include +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_ALIGNMENT +inline bool isAligned(size_t value) { + const size_t mask = sizeof(void*) - 1; + size_t addr = value; + return (addr & mask) == 0; +} +inline size_t addPadding(size_t bytes) { + const size_t mask = sizeof(void*) - 1; + return (bytes + mask) & ~mask; +} +template +struct AddPadding { + static const size_t mask = sizeof(void*) - 1; + static const size_t value = (bytes + mask) & ~mask; +}; +#else +inline bool isAligned(size_t) { + return true; +} +inline size_t addPadding(size_t bytes) { + return bytes; +} +template +struct AddPadding { + static const size_t value = bytes; +}; +#endif +template +inline bool isAligned(T* ptr) { + return isAligned(reinterpret_cast(ptr)); +} +template +inline T* addPadding(T* p) { + size_t address = addPadding(reinterpret_cast(p)); + return reinterpret_cast(address); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_DEBUG +#include +# define ARDUINOJSON_ASSERT(X) assert(X) +#else +# define ARDUINOJSON_ASSERT(X) ((void)0) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template Y)> +struct Max {}; +template +struct Max { + static const size_t value = X; +}; +template +struct Max { + static const size_t value = Y; +}; +template +struct conditional { + typedef TrueType type; +}; +template +struct conditional { + typedef FalseType type; +}; +template +struct enable_if {}; +template +struct enable_if { + typedef T type; +}; +template +struct integral_constant { + static const T value = v; +}; +typedef integral_constant true_type; +typedef integral_constant false_type; +template +struct is_array : false_type {}; +template +struct is_array : true_type {}; +template +struct is_array : true_type {}; +template +struct remove_reference { + typedef T type; +}; +template +struct remove_reference { + typedef T type; +}; +template +class is_base_of { + protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(const TBase*); + static char probe(...); + public: + static const bool value = + sizeof(probe(reinterpret_cast::type*>( + 0))) == sizeof(int); +}; +template +T&& declval(); +template +struct is_class { + protected: // <- to avoid GCC's "all member functions in class are private" + template + static int probe(void (U::*)(void)); + template + static char probe(...); + public: + static const bool value = sizeof(probe(0)) == sizeof(int); +}; +template +struct is_const : false_type {}; +template +struct is_const : true_type {}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4244) +#endif +#ifdef __ICCARM__ +#pragma diag_suppress=Pa093 +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct is_convertible { + protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(To); + static char probe(...); + static From& from_; + public: + static const bool value = sizeof(probe(from_)) == sizeof(int); +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +# pragma warning(pop) +#endif +#ifdef __ICCARM__ +#pragma diag_default=Pa093 +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct is_same : false_type {}; +template +struct is_same : true_type {}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct is_floating_point + : integral_constant< + bool, // + is_same::type>::value || + is_same::type>::value> {}; +template +struct is_integral : integral_constant::type, signed char>::value || + is_same::type, unsigned char>::value || + is_same::type, signed short>::value || + is_same::type, unsigned short>::value || + is_same::type, signed int>::value || + is_same::type, unsigned int>::value || + is_same::type, signed long>::value || + is_same::type, unsigned long>::value || + is_same::type, signed long long>::value || + is_same::type, unsigned long long>::value || + is_same::type, char>::value || + is_same::type, bool>::value> {}; +template +struct is_enum { + static const bool value = is_convertible::value && + !is_class::value && !is_integral::value && + !is_floating_point::value; +}; +template +struct is_pointer : false_type {}; +template +struct is_pointer : true_type {}; +template +struct is_signed : integral_constant::type, char>::value || + is_same::type, signed char>::value || + is_same::type, signed short>::value || + is_same::type, signed int>::value || + is_same::type, signed long>::value || + is_same::type, signed long long>::value || + is_same::type, float>::value || + is_same::type, double>::value> {}; +template +struct is_unsigned : integral_constant::type, unsigned char>::value || + is_same::type, unsigned short>::value || + is_same::type, unsigned int>::value || + is_same::type, unsigned long>::value || + is_same::type, unsigned long long>::value || + is_same::type, bool>::value> {}; +template +struct type_identity { + typedef T type; +}; +template +struct make_unsigned; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template <> +struct make_unsigned : type_identity {}; +template +struct make_void { + typedef void type; +}; +template +struct remove_const { + typedef T type; +}; +template +struct remove_const { + typedef T type; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#include +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +namespace StringStoragePolicy { +struct Link {}; +struct Copy {}; +struct LinkOrCopy { + bool link; +}; +} // namespace StringStoragePolicy +template +struct StringAdapter; +template +struct SizedStringAdapter; +template +typename StringAdapter::AdaptedString adaptString(const TString& s) { + return StringAdapter::adapt(s); +} +template +typename StringAdapter::AdaptedString adaptString(TChar* p) { + return StringAdapter::adapt(p); +} +template +typename SizedStringAdapter::AdaptedString adaptString(TChar* p, + size_t n) { + return SizedStringAdapter::adapt(p, n); +} +template +struct IsChar + : integral_constant::value && sizeof(T) == 1> {}; +class ZeroTerminatedRamString { + public: + static const size_t typeSortKey = 3; + ZeroTerminatedRamString(const char* str) : str_(str) {} + bool isNull() const { + return !str_; + } + size_t size() const { + return str_ ? ::strlen(str_) : 0; + } + char operator[](size_t i) const { + ARDUINOJSON_ASSERT(str_ != 0); + ARDUINOJSON_ASSERT(i <= size()); + return str_[i]; + } + const char* data() const { + return str_; + } + friend int stringCompare(ZeroTerminatedRamString a, + ZeroTerminatedRamString b) { + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + return ::strcmp(a.str_, b.str_); + } + friend bool stringEquals(ZeroTerminatedRamString a, + ZeroTerminatedRamString b) { + return stringCompare(a, b) == 0; + } + StringStoragePolicy::Copy storagePolicy() const { + return StringStoragePolicy::Copy(); + } + protected: + const char* str_; +}; +template +struct StringAdapter::value>::type> { + typedef ZeroTerminatedRamString AdaptedString; + static AdaptedString adapt(const TChar* p) { + return AdaptedString(reinterpret_cast(p)); + } +}; +template +struct StringAdapter::value>::type> { + typedef ZeroTerminatedRamString AdaptedString; + static AdaptedString adapt(const TChar* p) { + return AdaptedString(reinterpret_cast(p)); + } +}; +class StaticStringAdapter : public ZeroTerminatedRamString { + public: + StaticStringAdapter(const char* str) : ZeroTerminatedRamString(str) {} + StringStoragePolicy::Link storagePolicy() const { + return StringStoragePolicy::Link(); + } +}; +template <> +struct StringAdapter { + typedef StaticStringAdapter AdaptedString; + static AdaptedString adapt(const char* p) { + return AdaptedString(p); + } +}; +class SizedRamString { + public: + static const size_t typeSortKey = 2; + SizedRamString(const char* str, size_t sz) : str_(str), size_(sz) {} + bool isNull() const { + return !str_; + } + size_t size() const { + return size_; + } + char operator[](size_t i) const { + ARDUINOJSON_ASSERT(str_ != 0); + ARDUINOJSON_ASSERT(i <= size()); + return str_[i]; + } + const char* data() const { + return str_; + } + StringStoragePolicy::Copy storagePolicy() const { + return StringStoragePolicy::Copy(); + } + protected: + const char* str_; + size_t size_; +}; +template +struct SizedStringAdapter::value>::type> { + typedef SizedRamString AdaptedString; + static AdaptedString adapt(const TChar* p, size_t n) { + return AdaptedString(reinterpret_cast(p), n); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonString { + public: + enum Ownership { Copied, Linked }; + JsonString() : data_(0), size_(0), ownership_(Linked) {} + JsonString(const char* data, Ownership ownership = Linked) + : data_(data), size_(data ? ::strlen(data) : 0), ownership_(ownership) {} + JsonString(const char* data, size_t sz, Ownership ownership = Linked) + : data_(data), size_(sz), ownership_(ownership) {} + const char* c_str() const { + return data_; + } + bool isNull() const { + return !data_; + } + bool isLinked() const { + return ownership_ == Linked; + } + size_t size() const { + return size_; + } + explicit operator bool() const { + return data_ != 0; + } + friend bool operator==(JsonString lhs, JsonString rhs) { + if (lhs.size_ != rhs.size_) + return false; + if (lhs.data_ == rhs.data_) + return true; + if (!lhs.data_) + return false; + if (!rhs.data_) + return false; + return memcmp(lhs.data_, rhs.data_, lhs.size_) == 0; + } + friend bool operator!=(JsonString lhs, JsonString rhs) { + return !(lhs == rhs); + } +#if ARDUINOJSON_ENABLE_STD_STREAM + friend std::ostream& operator<<(std::ostream& lhs, const JsonString& rhs) { + lhs.write(rhs.c_str(), static_cast(rhs.size())); + return lhs; + } +#endif + private: + const char* data_; + size_t size_; + Ownership ownership_; +}; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class JsonStringAdapter : public SizedRamString { + public: + JsonStringAdapter(const JsonString& s) + : SizedRamString(s.c_str(), s.size()), linked_(s.isLinked()) {} + StringStoragePolicy::LinkOrCopy storagePolicy() const { + StringStoragePolicy::LinkOrCopy policy = {linked_}; + return policy; + } + private: + bool linked_; +}; +template <> +struct StringAdapter { + typedef JsonStringAdapter AdaptedString; + static AdaptedString adapt(const JsonString& s) { + return AdaptedString(s); + } +}; +namespace string_traits_impl { +template +struct has_cstr : false_type {}; +template +struct has_cstr().c_str()), + const char*>::value>::type> + : true_type {}; +template +struct has_data : false_type {}; +template +struct has_data().data()), + const char*>::value>::type> + : true_type {}; +template +struct has_length : false_type {}; +template +struct has_length< + T, typename enable_if< + is_same().length()), size_t>::value>::type> + : true_type {}; +template +struct has_size : false_type {}; +template +struct has_size< + T, typename enable_if< + is_same().size()), size_t>::value>::type> + : true_type {}; +} // namespace string_traits_impl +template +struct string_traits { + enum { + has_cstr = string_traits_impl::has_cstr::value, + has_length = string_traits_impl::has_length::value, + has_data = string_traits_impl::has_data::value, + has_size = string_traits_impl::has_size::value + }; +}; +template +struct StringAdapter< + T, + typename enable_if< + (string_traits::has_cstr || string_traits::has_data) && + (string_traits::has_length || string_traits::has_size)>::type> { + typedef SizedRamString AdaptedString; + static AdaptedString adapt(const T& s) { + return AdaptedString(get_data(s), get_size(s)); + } + private: + template + static typename enable_if::has_size, size_t>::type get_size( + const U& s) { + return s.size(); + } + template + static typename enable_if::has_size, size_t>::type get_size( + const U& s) { + return s.length(); + } + template + static typename enable_if::has_data, const char*>::type + get_data(const U& s) { + return s.data(); + } + template + static typename enable_if::has_data, const char*>::type + get_data(const U& s) { + return s.c_str(); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_PROGMEM +#ifdef ARDUINO +#else +class __FlashStringHelper; +#include +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +struct pgm_p { + pgm_p(const void* p) : address(reinterpret_cast(p)) {} + const char* address; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifndef strlen_P +inline size_t strlen_P(ArduinoJson::detail::pgm_p s) { + const char* p = s.address; + ARDUINOJSON_ASSERT(p != NULL); + while (pgm_read_byte(p)) + p++; + return size_t(p - s.address); +} +#endif +#ifndef strncmp_P +inline int strncmp_P(const char* a, ArduinoJson::detail::pgm_p b, size_t n) { + const char* s1 = a; + const char* s2 = b.address; + ARDUINOJSON_ASSERT(s1 != NULL); + ARDUINOJSON_ASSERT(s2 != NULL); + while (n-- > 0) { + char c1 = *s1++; + char c2 = static_cast(pgm_read_byte(s2++)); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (c1 == 0 /* and c2 as well */) + return 0; + } + return 0; +} +#endif +#ifndef strcmp_P +inline int strcmp_P(const char* a, ArduinoJson::detail::pgm_p b) { + const char* s1 = a; + const char* s2 = b.address; + ARDUINOJSON_ASSERT(s1 != NULL); + ARDUINOJSON_ASSERT(s2 != NULL); + for (;;) { + char c1 = *s1++; + char c2 = static_cast(pgm_read_byte(s2++)); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (c1 == 0 /* and c2 as well */) + return 0; + } +} +#endif +#ifndef memcmp_P +inline int memcmp_P(const void* a, ArduinoJson::detail::pgm_p b, size_t n) { + const uint8_t* p1 = reinterpret_cast(a); + const char* p2 = b.address; + ARDUINOJSON_ASSERT(p1 != NULL); + ARDUINOJSON_ASSERT(p2 != NULL); + while (n-- > 0) { + uint8_t v1 = *p1++; + uint8_t v2 = pgm_read_byte(p2++); + if (v1 != v2) + return v1 - v2; + } + return 0; +} +#endif +#ifndef memcpy_P +inline void* memcpy_P(void* dst, ArduinoJson::detail::pgm_p src, size_t n) { + uint8_t* d = reinterpret_cast(dst); + const char* s = src.address; + ARDUINOJSON_ASSERT(d != NULL); + ARDUINOJSON_ASSERT(s != NULL); + while (n-- > 0) { + *d++ = pgm_read_byte(s++); + } + return dst; +} +#endif +#ifndef pgm_read_dword +inline uint32_t pgm_read_dword(ArduinoJson::detail::pgm_p p) { + uint32_t result; + memcpy_P(&result, p.address, 4); + return result; +} +#endif +#ifndef pgm_read_float +inline float pgm_read_float(ArduinoJson::detail::pgm_p p) { + float result; + memcpy_P(&result, p.address, sizeof(float)); + return result; +} +#endif +#ifndef pgm_read_double +# if defined(__SIZEOF_DOUBLE__) && defined(__SIZEOF_FLOAT__) && \ + __SIZEOF_DOUBLE__ == __SIZEOF_FLOAT__ +inline double pgm_read_double(ArduinoJson::detail::pgm_p p) { + return pgm_read_float(p.address); +} +# else +inline double pgm_read_double(ArduinoJson::detail::pgm_p p) { + double result; + memcpy_P(&result, p.address, sizeof(double)); + return result; +} +# endif +#endif +#ifndef pgm_read_ptr +inline void* pgm_read_ptr(ArduinoJson::detail::pgm_p p) { + void* result; + memcpy_P(&result, p.address, sizeof(result)); + return result; +} +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class FlashString { + public: + static const size_t typeSortKey = 1; + FlashString(const __FlashStringHelper* str, size_t sz) + : str_(reinterpret_cast(str)), size_(sz) {} + bool isNull() const { + return !str_; + } + char operator[](size_t i) const { + ARDUINOJSON_ASSERT(str_ != 0); + ARDUINOJSON_ASSERT(i <= size_); + return static_cast(pgm_read_byte(str_ + i)); + } + size_t size() const { + return size_; + } + friend bool stringEquals(FlashString a, SizedRamString b) { + ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + if (a.size() != b.size()) + return false; + return ::memcmp_P(b.data(), a.str_, a.size_) == 0; + } + friend int stringCompare(FlashString a, SizedRamString b) { + ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); + ARDUINOJSON_ASSERT(!a.isNull()); + ARDUINOJSON_ASSERT(!b.isNull()); + size_t minsize = a.size() < b.size() ? a.size() : b.size(); + int res = ::memcmp_P(b.data(), a.str_, minsize); + if (res) + return -res; + if (a.size() < b.size()) + return -1; + if (a.size() > b.size()) + return 1; + return 0; + } + friend void stringGetChars(FlashString s, char* p, size_t n) { + ARDUINOJSON_ASSERT(s.size() <= n); + ::memcpy_P(p, s.str_, n); + } + StringStoragePolicy::Copy storagePolicy() const { + return StringStoragePolicy::Copy(); + } + private: + const char* str_; + size_t size_; +}; +template <> +struct StringAdapter { + typedef FlashString AdaptedString; + static AdaptedString adapt(const __FlashStringHelper* s) { + return AdaptedString(s, s ? strlen_P(reinterpret_cast(s)) : 0); + } +}; +template <> +struct SizedStringAdapter { + typedef FlashString AdaptedString; + static AdaptedString adapt(const __FlashStringHelper* s, size_t n) { + return AdaptedString(s, n); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +typename enable_if::type +stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { + ARDUINOJSON_ASSERT(!s1.isNull()); + ARDUINOJSON_ASSERT(!s2.isNull()); + size_t size1 = s1.size(); + size_t size2 = s2.size(); + size_t n = size1 < size2 ? size1 : size2; + for (size_t i = 0; i < n; i++) { + if (s1[i] != s2[i]) + return s1[i] - s2[i]; + } + if (size1 < size2) + return -1; + if (size1 > size2) + return 1; + return 0; +} +template +typename enable_if< + (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), int>::type +stringCompare(TAdaptedString1 s1, TAdaptedString2 s2) { + return -stringCompare(s2, s1); +} +template +typename enable_if::type +stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { + ARDUINOJSON_ASSERT(!s1.isNull()); + ARDUINOJSON_ASSERT(!s2.isNull()); + size_t size1 = s1.size(); + size_t size2 = s2.size(); + if (size1 != size2) + return false; + for (size_t i = 0; i < size1; i++) { + if (s1[i] != s2[i]) + return false; + } + return true; +} +template +typename enable_if< + (TAdaptedString1::typeSortKey > TAdaptedString2::typeSortKey), bool>::type +stringEquals(TAdaptedString1 s1, TAdaptedString2 s2) { + return stringEquals(s2, s1); +} +template +static void stringGetChars(TAdaptedString s, char* p, size_t n) { + ARDUINOJSON_ASSERT(s.size() <= n); + for (size_t i = 0; i < n; i++) { + p[i] = s[i]; + } +} +template +struct int_t; +template <> +struct int_t<8> { + typedef int8_t type; +}; +template <> +struct int_t<16> { + typedef int16_t type; +}; +template <> +struct int_t<32> { + typedef int32_t type; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4310) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct numeric_limits; +template +struct numeric_limits::value>::type> { + static T lowest() { + return 0; + } + static T highest() { + return T(-1); + } +}; +template +struct numeric_limits< + T, typename enable_if::value && is_signed::value>::type> { + static T lowest() { + return T(T(1) << (sizeof(T) * 8 - 1)); + } + static T highest() { + return T(~lowest()); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER +# pragma warning(pop) +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class MemoryPool; +class VariantData; +class VariantSlot; +class CollectionData { + VariantSlot* head_; + VariantSlot* tail_; + public: + VariantData* addElement(MemoryPool* pool); + VariantData* getElement(size_t index) const; + VariantData* getOrAddElement(size_t index, MemoryPool* pool); + void removeElement(size_t index); + template + VariantData* addMember(TAdaptedString key, MemoryPool* pool); + template + VariantData* getMember(TAdaptedString key) const; + template + VariantData* getOrAddMember(TAdaptedString key, MemoryPool* pool); + template + void removeMember(TAdaptedString key) { + removeSlot(getSlot(key)); + } + template + bool containsKey(const TAdaptedString& key) const; + void clear(); + size_t memoryUsage() const; + size_t size() const; + VariantSlot* addSlot(MemoryPool*); + void removeSlot(VariantSlot* slot); + bool copyFrom(const CollectionData& src, MemoryPool* pool); + VariantSlot* head() const { + return head_; + } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance); + private: + VariantSlot* getSlot(size_t index) const; + template + VariantSlot* getSlot(TAdaptedString key) const; + VariantSlot* getPreviousSlot(VariantSlot*) const; +}; +inline const VariantData* collectionToVariant( + const CollectionData* collection) { + const void* data = collection; // prevent warning cast-align + return reinterpret_cast(data); +} +inline VariantData* collectionToVariant(CollectionData* collection) { + void* data = collection; // prevent warning cast-align + return reinterpret_cast(data); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +#if ARDUINOJSON_USE_DOUBLE +typedef double JsonFloat; +#else +typedef float JsonFloat; +#endif +#if ARDUINOJSON_USE_LONG_LONG +typedef int64_t JsonInteger; +typedef uint64_t JsonUInt; +#else +typedef long JsonInteger; +typedef unsigned long JsonUInt; +#endif +ARDUINOJSON_END_PUBLIC_NAMESPACE +#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ + static_assert(sizeof(T) <= sizeof(ArduinoJson::JsonInteger), \ + "To use 64-bit integers with ArduinoJson, you must set " \ + "ARDUINOJSON_USE_LONG_LONG to 1. See " \ + "https://arduinojson.org/v6/api/config/use_long_long/"); +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +enum { + VALUE_MASK = 0x7F, + OWNED_VALUE_BIT = 0x01, + VALUE_IS_NULL = 0, + VALUE_IS_LINKED_RAW = 0x02, + VALUE_IS_OWNED_RAW = 0x03, + VALUE_IS_LINKED_STRING = 0x04, + VALUE_IS_OWNED_STRING = 0x05, + VALUE_IS_BOOLEAN = 0x06, + NUMBER_BIT = 0x08, + VALUE_IS_UNSIGNED_INTEGER = 0x08, + VALUE_IS_SIGNED_INTEGER = 0x0A, + VALUE_IS_FLOAT = 0x0C, + COLLECTION_MASK = 0x60, + VALUE_IS_OBJECT = 0x20, + VALUE_IS_ARRAY = 0x40, + OWNED_KEY_BIT = 0x80 +}; +struct RawData { + const char* data; + size_t size; +}; +union VariantContent { + JsonFloat asFloat; + bool asBoolean; + JsonUInt asUnsignedInteger; + JsonInteger asSignedInteger; + CollectionData asCollection; + struct { + const char* data; + size_t size; + } asString; +}; +typedef int_t::type VariantSlotDiff; +class VariantSlot { + VariantContent content_; + uint8_t flags_; + VariantSlotDiff next_; + const char* key_; + public: + VariantData* data() { + return reinterpret_cast(&content_); + } + const VariantData* data() const { + return reinterpret_cast(&content_); + } + VariantSlot* next() { + return next_ ? this + next_ : 0; + } + const VariantSlot* next() const { + return const_cast(this)->next(); + } + VariantSlot* next(size_t distance) { + VariantSlot* slot = this; + while (distance--) { + if (!slot->next_) + return 0; + slot += slot->next_; + } + return slot; + } + const VariantSlot* next(size_t distance) const { + return const_cast(this)->next(distance); + } + void setNext(VariantSlot* slot) { + ARDUINOJSON_ASSERT(!slot || slot - this >= + numeric_limits::lowest()); + ARDUINOJSON_ASSERT(!slot || slot - this <= + numeric_limits::highest()); + next_ = VariantSlotDiff(slot ? slot - this : 0); + } + void setNextNotNull(VariantSlot* slot) { + ARDUINOJSON_ASSERT(slot != 0); + ARDUINOJSON_ASSERT(slot - this >= + numeric_limits::lowest()); + ARDUINOJSON_ASSERT(slot - this <= + numeric_limits::highest()); + next_ = VariantSlotDiff(slot - this); + } + void setKey(JsonString k) { + ARDUINOJSON_ASSERT(k); + if (k.isLinked()) + flags_ &= VALUE_MASK; + else + flags_ |= OWNED_KEY_BIT; + key_ = k.c_str(); + } + const char* key() const { + return key_; + } + bool ownsKey() const { + return (flags_ & OWNED_KEY_BIT) != 0; + } + void clear() { + next_ = 0; + flags_ = 0; + key_ = 0; + } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { + if (flags_ & OWNED_KEY_BIT) + key_ += stringDistance; + if (flags_ & OWNED_VALUE_BIT) + content_.asString.data += stringDistance; + if (flags_ & COLLECTION_MASK) + content_.asCollection.movePointers(stringDistance, variantDistance); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#define JSON_STRING_SIZE(SIZE) (SIZE + 1) +#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ + ((NUMBER_OF_ELEMENTS) * sizeof(ArduinoJson::detail::VariantSlot)) +#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \ + ((NUMBER_OF_ELEMENTS) * sizeof(ArduinoJson::detail::VariantSlot)) +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class MemoryPool { + public: + MemoryPool(char* buf, size_t capa) + : begin_(buf), + left_(buf), + right_(buf ? buf + capa : 0), + end_(buf ? buf + capa : 0), + overflowed_(false) { + ARDUINOJSON_ASSERT(isAligned(begin_)); + ARDUINOJSON_ASSERT(isAligned(right_)); + ARDUINOJSON_ASSERT(isAligned(end_)); + } + void* buffer() { + return begin_; // NOLINT(clang-analyzer-unix.Malloc) + } + size_t capacity() const { + return size_t(end_ - begin_); + } + size_t size() const { + return size_t(left_ - begin_ + end_ - right_); + } + bool overflowed() const { + return overflowed_; + } + VariantSlot* allocVariant() { + return allocRight(); + } + template + const char* saveString(TAdaptedString str) { + if (str.isNull()) + return 0; +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + const char* existingCopy = findString(str); + if (existingCopy) + return existingCopy; +#endif + size_t n = str.size(); + char* newCopy = allocString(n + 1); + if (newCopy) { + stringGetChars(str, newCopy, n); + newCopy[n] = 0; // force null-terminator + } + return newCopy; + } + void getFreeZone(char** zoneStart, size_t* zoneSize) const { + *zoneStart = left_; + *zoneSize = size_t(right_ - left_); + } + const char* saveStringFromFreeZone(size_t len) { +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + const char* dup = findString(adaptString(left_, len)); + if (dup) + return dup; +#endif + const char* str = left_; + left_ += len; + *left_++ = 0; + checkInvariants(); + return str; + } + void markAsOverflowed() { + overflowed_ = true; + } + void clear() { + left_ = begin_; + right_ = end_; + overflowed_ = false; + } + bool canAlloc(size_t bytes) const { + return left_ + bytes <= right_; + } + bool owns(void* p) const { + return begin_ <= p && p < end_; + } + void* operator new(size_t, void* p) { + return p; + } + ptrdiff_t squash() { + char* new_right = addPadding(left_); + if (new_right >= right_) + return 0; + size_t right_size = static_cast(end_ - right_); + memmove(new_right, right_, right_size); + ptrdiff_t bytes_reclaimed = right_ - new_right; + right_ = new_right; + end_ = new_right + right_size; + return bytes_reclaimed; + } + void movePointers(ptrdiff_t offset) { + begin_ += offset; + left_ += offset; + right_ += offset; + end_ += offset; + } + private: + void checkInvariants() { + ARDUINOJSON_ASSERT(begin_ <= left_); + ARDUINOJSON_ASSERT(left_ <= right_); + ARDUINOJSON_ASSERT(right_ <= end_); + ARDUINOJSON_ASSERT(isAligned(right_)); + } +#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION + template + const char* findString(const TAdaptedString& str) const { + size_t n = str.size(); + for (char* next = begin_; next + n < left_; ++next) { + if (next[n] == '\0' && stringEquals(str, adaptString(next, n))) + return next; + while (*next) + ++next; + } + return 0; + } +#endif + char* allocString(size_t n) { + if (!canAlloc(n)) { + overflowed_ = true; + return 0; + } + char* s = left_; + left_ += n; + checkInvariants(); + return s; + } + template + T* allocRight() { + return reinterpret_cast(allocRight(sizeof(T))); + } + void* allocRight(size_t bytes) { + if (!canAlloc(bytes)) { + overflowed_ = true; + return 0; + } + right_ -= bytes; + return right_; + } + char *begin_, *left_, *right_, *end_; + bool overflowed_; +}; +template +bool storeString(MemoryPool* pool, TAdaptedString str, + StringStoragePolicy::Copy, TCallback callback) { + const char* copy = pool->saveString(str); + JsonString storedString(copy, str.size(), JsonString::Copied); + callback(storedString); + return copy != 0; +} +template +bool storeString(MemoryPool*, TAdaptedString str, StringStoragePolicy::Link, + TCallback callback) { + JsonString storedString(str.data(), str.size(), JsonString::Linked); + callback(storedString); + return !str.isNull(); +} +template +bool storeString(MemoryPool* pool, TAdaptedString str, + StringStoragePolicy::LinkOrCopy policy, TCallback callback) { + if (policy.link) + return storeString(pool, str, StringStoragePolicy::Link(), callback); + else + return storeString(pool, str, StringStoragePolicy::Copy(), callback); +} +template +bool storeString(MemoryPool* pool, TAdaptedString str, TCallback callback) { + return storeString(pool, str, str.storagePolicy(), callback); +} +template +struct IsString : false_type {}; +template +struct IsString< + T, typename make_void::AdaptedString>::type> + : true_type {}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +#ifdef _MSC_VER // Visual Studio +# define FORCE_INLINE // __forceinline causes C4714 when returning std::string +# define NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) // GCC or Clang +# define FORCE_INLINE __attribute__((always_inline)) +# define NO_INLINE __attribute__((noinline)) +#else // Other compilers +# define FORCE_INLINE +# define NO_INLINE +#endif +#if defined(__has_attribute) +# if __has_attribute(no_sanitize) +# define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) +# else +# define ARDUINOJSON_NO_SANITIZE(check) +# endif +#else +# define ARDUINOJSON_NO_SANITIZE(check) +#endif +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonArray; +class JsonObject; +class JsonVariant; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +struct VariantTo {}; +template <> +struct VariantTo { + typedef JsonArray type; +}; +template <> +struct VariantTo { + typedef JsonObject type; +}; +template <> +struct VariantTo { + typedef JsonVariant type; +}; +class VariantAttorney { + template + struct ResultOfGetData { + protected: // <- to avoid GCC's "all member functions in class are private" + static int probe(const VariantData*); + static char probe(VariantData*); + static TClient& client; + public: + typedef typename conditional::type type; + }; + public: + template + FORCE_INLINE static MemoryPool* getPool(TClient& client) { + return client.getPool(); + } + template + FORCE_INLINE static typename ResultOfGetData::type getData( + TClient& client) { + return client.getData(); + } + template + FORCE_INLINE static VariantData* getOrCreateData(TClient& client) { + return client.getOrCreateData(); + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +class SerializedValue { + public: + explicit SerializedValue(T str) : str_(str) {} + operator T() const { + return str_; + } + const char* data() const { + return str_.c_str(); + } + size_t size() const { + return str_.length(); + } + private: + T str_; +}; +template +class SerializedValue { + public: + explicit SerializedValue(TChar* p, size_t n) : data_(p), size_(n) {} + operator TChar*() const { + return data_; + } + TChar* data() const { + return data_; + } + size_t size() const { + return size_; + } + private: + TChar* data_; + size_t size_; +}; +template +inline SerializedValue serialized(T str) { + return SerializedValue(str); +} +template +inline SerializedValue serialized(TChar* p) { + return SerializedValue(p, detail::adaptString(p).size()); +} +template +inline SerializedValue serialized(TChar* p, size_t n) { + return SerializedValue(p, n); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wconversion" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +#ifndef isnan +template +bool isnan(T x) { + return x != x; +} +#endif +#ifndef isinf +template +bool isinf(T x) { + return x != 0.0 && x * 2 == x; +} +#endif +template +struct alias_cast_t { + union { + F raw; + T data; + }; +}; +template +T alias_cast(F raw_data) { + alias_cast_t ac; + ac.raw = raw_data; + return ac.data; +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_PROGMEM +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +#if ARDUINOJSON_ENABLE_PROGMEM +# ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY +# define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \ + static type const name[] PROGMEM = __VA_ARGS__; +# endif +template +inline const T* pgm_read(const T* const* p) { + return reinterpret_cast(pgm_read_ptr(p)); +} +inline uint32_t pgm_read(const uint32_t* p) { + return pgm_read_dword(p); +} +inline double pgm_read(const double* p) { + return pgm_read_double(p); +} +inline float pgm_read(const float* p) { + return pgm_read_float(p); +} +#else +# ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY +# define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \ + static type const name[] = __VA_ARGS__; +# endif +template +inline T pgm_read(const T* p) { + return *p; +} +#endif +template +class pgm_ptr { + public: + explicit pgm_ptr(const T* ptr) : ptr_(ptr) {} + T operator[](intptr_t index) const { + return pgm_read(ptr_ + index); + } + private: + const T* ptr_; +}; +template +struct FloatTraits {}; +template +struct FloatTraits { + typedef uint64_t mantissa_type; + static const short mantissa_bits = 52; + static const mantissa_type mantissa_max = + (mantissa_type(1) << mantissa_bits) - 1; + typedef int16_t exponent_type; + static const exponent_type exponent_max = 308; + static pgm_ptr positiveBinaryPowersOfTen() { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // + uint64_t, factors, + { + 0x4024000000000000, // 1e1 + 0x4059000000000000, // 1e2 + 0x40C3880000000000, // 1e4 + 0x4197D78400000000, // 1e8 + 0x4341C37937E08000, // 1e16 + 0x4693B8B5B5056E17, // 1e32 + 0x4D384F03E93FF9F5, // 1e64 + 0x5A827748F9301D32, // 1e128 + 0x75154FDD7F73BF3C, // 1e256 + }); + return pgm_ptr(reinterpret_cast(factors)); + } + static pgm_ptr negativeBinaryPowersOfTen() { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // + uint64_t, factors, + { + 0x3FB999999999999A, // 1e-1 + 0x3F847AE147AE147B, // 1e-2 + 0x3F1A36E2EB1C432D, // 1e-4 + 0x3E45798EE2308C3A, // 1e-8 + 0x3C9CD2B297D889BC, // 1e-16 + 0x3949F623D5A8A733, // 1e-32 + 0x32A50FFD44F4A73D, // 1e-64 + 0x255BBA08CF8C979D, // 1e-128 + 0x0AC8062864AC6F43 // 1e-256 + }); + return pgm_ptr(reinterpret_cast(factors)); + } + static T nan() { + return forge(0x7ff8000000000000); + } + static T inf() { + return forge(0x7ff0000000000000); + } + static T highest() { + return forge(0x7FEFFFFFFFFFFFFF); + } + template // int64_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 8, + signed>::type* = 0) { + return forge(0x43DFFFFFFFFFFFFF); // 9.2233720368547748e+18 + } + template // uint64_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 8, + unsigned>::type* = 0) { + return forge(0x43EFFFFFFFFFFFFF); // 1.8446744073709549568e+19 + } + static T lowest() { + return forge(0xFFEFFFFFFFFFFFFF); + } + static T forge(uint64_t bits) { + return alias_cast(bits); + } +}; +template +struct FloatTraits { + typedef uint32_t mantissa_type; + static const short mantissa_bits = 23; + static const mantissa_type mantissa_max = + (mantissa_type(1) << mantissa_bits) - 1; + typedef int8_t exponent_type; + static const exponent_type exponent_max = 38; + static pgm_ptr positiveBinaryPowersOfTen() { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, + { + 0x41200000, // 1e1f + 0x42c80000, // 1e2f + 0x461c4000, // 1e4f + 0x4cbebc20, // 1e8f + 0x5a0e1bca, // 1e16f + 0x749dc5ae // 1e32f + }); + return pgm_ptr(reinterpret_cast(factors)); + } + static pgm_ptr negativeBinaryPowersOfTen() { + ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors, + { + 0x3dcccccd, // 1e-1f + 0x3c23d70a, // 1e-2f + 0x38d1b717, // 1e-4f + 0x322bcc77, // 1e-8f + 0x24e69595, // 1e-16f + 0x0a4fb11f // 1e-32f + }); + return pgm_ptr(reinterpret_cast(factors)); + } + static T forge(uint32_t bits) { + return alias_cast(bits); + } + static T nan() { + return forge(0x7fc00000); + } + static T inf() { + return forge(0x7f800000); + } + static T highest() { + return forge(0x7f7fffff); + } + template // int32_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 4, + signed>::type* = 0) { + return forge(0x4EFFFFFF); // 2.14748352E9 + } + template // uint32_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 4, + unsigned>::type* = 0) { + return forge(0x4F7FFFFF); // 4.29496704E9 + } + template // int64_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 8, + signed>::type* = 0) { + return forge(0x5EFFFFFF); // 9.22337148709896192E18 + } + template // uint64_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 8, + unsigned>::type* = 0) { + return forge(0x5F7FFFFF); // 1.844674297419792384E19 + } + static T lowest() { + return forge(0xFf7fffff); + } +}; +template +inline TFloat make_float(TFloat m, TExponent e) { + using traits = FloatTraits; + auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen() + : traits::negativeBinaryPowersOfTen(); + if (e <= 0) + e = TExponent(-e); + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) + m *= powersOfTen[index]; + e >>= 1; + } + return m; +} +template +typename enable_if::value && is_unsigned::value && + is_integral::value && sizeof(TOut) <= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + return value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_unsigned::value && + is_integral::value && sizeof(TIn) < sizeof(TOut), + bool>::type +canConvertNumber(TIn) { + return true; +} +template +typename enable_if::value && is_floating_point::value, + bool>::type +canConvertNumber(TIn) { + return true; +} +template +typename enable_if::value && is_signed::value && + is_integral::value && is_signed::value && + sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + return value >= TIn(numeric_limits::lowest()) && + value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_signed::value && + is_integral::value && is_signed::value && + sizeof(TIn) <= sizeof(TOut), + bool>::type +canConvertNumber(TIn) { + return true; +} +template +typename enable_if::value && is_signed::value && + is_integral::value && is_unsigned::value && + sizeof(TOut) >= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + if (value < 0) + return false; + return TOut(value) <= numeric_limits::highest(); +} +template +typename enable_if::value && is_signed::value && + is_integral::value && is_unsigned::value && + sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + if (value < 0) + return false; + return value <= TIn(numeric_limits::highest()); +} +template +typename enable_if::value && is_integral::value && + sizeof(TOut) < sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + return value >= numeric_limits::lowest() && + value <= numeric_limits::highest(); +} +template +typename enable_if::value && is_integral::value && + sizeof(TOut) >= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + return value >= numeric_limits::lowest() && + value <= FloatTraits::template highest_for(); +} +template +TOut convertNumber(TIn value) { + return canConvertNumber(value) ? TOut(value) : 0; +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +class VariantData { + VariantContent content_; // must be first to allow cast from array to variant + uint8_t flags_; + public: + VariantData() : flags_(VALUE_IS_NULL) {} + void operator=(const VariantData& src) { + content_ = src.content_; + flags_ = uint8_t((flags_ & OWNED_KEY_BIT) | (src.flags_ & ~OWNED_KEY_BIT)); + } + template + typename TVisitor::result_type accept(TVisitor& visitor) const { + switch (type()) { + case VALUE_IS_FLOAT: + return visitor.visitFloat(content_.asFloat); + case VALUE_IS_ARRAY: + return visitor.visitArray(content_.asCollection); + case VALUE_IS_OBJECT: + return visitor.visitObject(content_.asCollection); + case VALUE_IS_LINKED_STRING: + case VALUE_IS_OWNED_STRING: + return visitor.visitString(content_.asString.data, + content_.asString.size); + case VALUE_IS_OWNED_RAW: + case VALUE_IS_LINKED_RAW: + return visitor.visitRawJson(content_.asString.data, + content_.asString.size); + case VALUE_IS_SIGNED_INTEGER: + return visitor.visitSignedInteger(content_.asSignedInteger); + case VALUE_IS_UNSIGNED_INTEGER: + return visitor.visitUnsignedInteger(content_.asUnsignedInteger); + case VALUE_IS_BOOLEAN: + return visitor.visitBoolean(content_.asBoolean != 0); + default: + return visitor.visitNull(); + } + } + template + T asIntegral() const; + template + T asFloat() const; + JsonString asString() const; + bool asBoolean() const; + CollectionData* asArray() { + return isArray() ? &content_.asCollection : 0; + } + const CollectionData* asArray() const { + return const_cast(this)->asArray(); + } + const CollectionData* asCollection() const { + return isCollection() ? &content_.asCollection : 0; + } + CollectionData* asObject() { + return isObject() ? &content_.asCollection : 0; + } + const CollectionData* asObject() const { + return const_cast(this)->asObject(); + } + bool copyFrom(const VariantData& src, MemoryPool* pool); + bool isArray() const { + return (flags_ & VALUE_IS_ARRAY) != 0; + } + bool isBoolean() const { + return type() == VALUE_IS_BOOLEAN; + } + bool isCollection() const { + return (flags_ & COLLECTION_MASK) != 0; + } + template + bool isInteger() const { + switch (type()) { + case VALUE_IS_UNSIGNED_INTEGER: + return canConvertNumber(content_.asUnsignedInteger); + case VALUE_IS_SIGNED_INTEGER: + return canConvertNumber(content_.asSignedInteger); + default: + return false; + } + } + bool isFloat() const { + return (flags_ & NUMBER_BIT) != 0; + } + bool isString() const { + return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING; + } + bool isObject() const { + return (flags_ & VALUE_IS_OBJECT) != 0; + } + bool isNull() const { + return type() == VALUE_IS_NULL; + } + bool isEnclosed() const { + return !isFloat(); + } + void remove(size_t index) { + if (isArray()) + content_.asCollection.removeElement(index); + } + template + void remove(TAdaptedString key) { + if (isObject()) + content_.asCollection.removeMember(key); + } + void setBoolean(bool value) { + setType(VALUE_IS_BOOLEAN); + content_.asBoolean = value; + } + void setFloat(JsonFloat value) { + setType(VALUE_IS_FLOAT); + content_.asFloat = value; + } + void setLinkedRaw(SerializedValue value) { + if (value.data()) { + setType(VALUE_IS_LINKED_RAW); + content_.asString.data = value.data(); + content_.asString.size = value.size(); + } else { + setType(VALUE_IS_NULL); + } + } + template + bool storeOwnedRaw(SerializedValue value, MemoryPool* pool) { + const char* dup = pool->saveString(adaptString(value.data(), value.size())); + if (dup) { + setType(VALUE_IS_OWNED_RAW); + content_.asString.data = dup; + content_.asString.size = value.size(); + return true; + } else { + setType(VALUE_IS_NULL); + return false; + } + } + template + typename enable_if::value>::type setInteger(T value) { + setType(VALUE_IS_UNSIGNED_INTEGER); + content_.asUnsignedInteger = static_cast(value); + } + template + typename enable_if::value>::type setInteger(T value) { + setType(VALUE_IS_SIGNED_INTEGER); + content_.asSignedInteger = value; + } + void setNull() { + setType(VALUE_IS_NULL); + } + void setString(JsonString s) { + ARDUINOJSON_ASSERT(s); + if (s.isLinked()) + setType(VALUE_IS_LINKED_STRING); + else + setType(VALUE_IS_OWNED_STRING); + content_.asString.data = s.c_str(); + content_.asString.size = s.size(); + } + CollectionData& toArray() { + setType(VALUE_IS_ARRAY); + content_.asCollection.clear(); + return content_.asCollection; + } + CollectionData& toObject() { + setType(VALUE_IS_OBJECT); + content_.asCollection.clear(); + return content_.asCollection; + } + size_t memoryUsage() const { + switch (type()) { + case VALUE_IS_OWNED_STRING: + case VALUE_IS_OWNED_RAW: + return content_.asString.size + 1; + case VALUE_IS_OBJECT: + case VALUE_IS_ARRAY: + return content_.asCollection.memoryUsage(); + default: + return 0; + } + } + size_t size() const { + return isCollection() ? content_.asCollection.size() : 0; + } + VariantData* addElement(MemoryPool* pool) { + if (isNull()) + toArray(); + if (!isArray()) + return 0; + return content_.asCollection.addElement(pool); + } + VariantData* getElement(size_t index) const { + const CollectionData* col = asArray(); + return col ? col->getElement(index) : 0; + } + VariantData* getOrAddElement(size_t index, MemoryPool* pool) { + if (isNull()) + toArray(); + if (!isArray()) + return 0; + return content_.asCollection.getOrAddElement(index, pool); + } + template + VariantData* getMember(TAdaptedString key) const { + const CollectionData* col = asObject(); + return col ? col->getMember(key) : 0; + } + template + VariantData* getOrAddMember(TAdaptedString key, MemoryPool* pool) { + if (isNull()) + toObject(); + if (!isObject()) + return 0; + return content_.asCollection.getOrAddMember(key, pool); + } + void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { + if (flags_ & OWNED_VALUE_BIT) + content_.asString.data += stringDistance; + if (flags_ & COLLECTION_MASK) + content_.asCollection.movePointers(stringDistance, variantDistance); + } + uint8_t type() const { + return flags_ & VALUE_MASK; + } + template + inline bool setString(TAdaptedString value, MemoryPool* pool) { + if (value.isNull()) { + setNull(); + return true; + } + return storeString(pool, value, VariantStringSetter(this)); + } + private: + void setType(uint8_t t) { + flags_ &= OWNED_KEY_BIT; + flags_ |= t; + } + struct VariantStringSetter { + VariantStringSetter(VariantData* instance) : instance_(instance) {} + template + void operator()(TStoredString s) { + if (s) + instance_->setString(s); + else + instance_->setNull(); + } + VariantData* instance_; + }; +}; +template +struct Visitor { + typedef TResult result_type; + TResult visitArray(const CollectionData&) { + return TResult(); + } + TResult visitBoolean(bool) { + return TResult(); + } + TResult visitFloat(JsonFloat) { + return TResult(); + } + TResult visitSignedInteger(JsonInteger) { + return TResult(); + } + TResult visitNull() { + return TResult(); + } + TResult visitObject(const CollectionData&) { + return TResult(); + } + TResult visitUnsignedInteger(JsonUInt) { + return TResult(); + } + TResult visitRawJson(const char*, size_t) { + return TResult(); + } + TResult visitString(const char*, size_t) { + return TResult(); + } +}; +template +inline typename TVisitor::result_type variantAccept(const VariantData* var, + TVisitor& visitor) { + if (var != 0) + return var->accept(visitor); + else + return visitor.visitNull(); +} +inline bool variantCopyFrom(VariantData* dst, const VariantData* src, + MemoryPool* pool) { + if (!dst) + return false; + if (!src) { + dst->setNull(); + return true; + } + return dst->copyFrom(*src, pool); +} +inline void variantSetNull(VariantData* var) { + if (!var) + return; + var->setNull(); +} +template +inline bool variantSetString(VariantData* var, TAdaptedString value, + MemoryPool* pool) { + return var != 0 ? var->setString(value, pool) : 0; +} +inline size_t variantSize(const VariantData* var) { + return var != 0 ? var->size() : 0; +} +inline CollectionData* variantToArray(VariantData* var) { + if (!var) + return 0; + return &var->toArray(); +} +inline CollectionData* variantToObject(VariantData* var) { + if (!var) + return 0; + return &var->toObject(); +} +inline VariantData* variantGetElement(const VariantData* var, size_t index) { + return var != 0 ? var->getElement(index) : 0; +} +inline NO_INLINE VariantData* variantAddElement(VariantData* var, + MemoryPool* pool) { + return var != 0 ? var->addElement(pool) : 0; +} +inline NO_INLINE VariantData* variantGetOrAddElement(VariantData* var, + size_t index, + MemoryPool* pool) { + return var != 0 ? var->getOrAddElement(index, pool) : 0; +} +template +VariantData* variantGetMember(const VariantData* var, TAdaptedString key) { + if (!var) + return 0; + return var->getMember(key); +} +template +VariantData* variantGetOrAddMember(VariantData* var, TAdaptedString key, + MemoryPool* pool) { + if (!var) + return 0; + return var->getOrAddMember(key, pool); +} +inline bool variantIsNull(const VariantData* var) { + return var == 0 || var->isNull(); +} +inline size_t variantNesting(const VariantData* var) { + if (!var) + return 0; + const CollectionData* collection = var->asCollection(); + if (!collection) + return 0; + size_t maxChildNesting = 0; + for (const VariantSlot* s = collection->head(); s; s = s->next()) { + size_t childNesting = variantNesting(s->data()); + if (childNesting > maxChildNesting) + maxChildNesting = childNesting; + } + return maxChildNesting + 1; +} +enum CompareResult { + COMPARE_RESULT_DIFFER = 0, + COMPARE_RESULT_EQUAL = 1, + COMPARE_RESULT_GREATER = 2, + COMPARE_RESULT_LESS = 4, + COMPARE_RESULT_GREATER_OR_EQUAL = 3, + COMPARE_RESULT_LESS_OR_EQUAL = 5 +}; +template +CompareResult arithmeticCompare(const T& lhs, const T& rhs) { + if (lhs < rhs) + return COMPARE_RESULT_LESS; + else if (lhs > rhs) + return COMPARE_RESULT_GREATER; + else + return COMPARE_RESULT_EQUAL; +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + sizeof(T1) < sizeof(T2)>::type* = 0) { + return arithmeticCompare(static_cast(lhs), rhs); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + sizeof(T2) < sizeof(T1)>::type* = 0) { + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + is_signed::value == is_signed::value && + sizeof(T2) == sizeof(T1)>::type* = 0) { + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + is_unsigned::value && is_signed::value && + sizeof(T2) == sizeof(T1)>::type* = 0) { + if (rhs < 0) + return COMPARE_RESULT_GREATER; + return arithmeticCompare(lhs, static_cast(rhs)); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value && is_integral::value && + is_signed::value && is_unsigned::value && + sizeof(T2) == sizeof(T1)>::type* = 0) { + if (lhs < 0) + return COMPARE_RESULT_LESS; + return arithmeticCompare(static_cast(lhs), rhs); +} +template +CompareResult arithmeticCompare( + const T1& lhs, const T2& rhs, + typename enable_if::value || + is_floating_point::value>::type* = 0) { + return arithmeticCompare(static_cast(lhs), + static_cast(rhs)); +} +template +CompareResult arithmeticCompareNegateLeft( + JsonUInt, const T2&, + typename enable_if::value>::type* = 0) { + return COMPARE_RESULT_LESS; +} +template +CompareResult arithmeticCompareNegateLeft( + JsonUInt lhs, const T2& rhs, + typename enable_if::value>::type* = 0) { + if (rhs > 0) + return COMPARE_RESULT_LESS; + return arithmeticCompare(-rhs, static_cast(lhs)); +} +template +CompareResult arithmeticCompareNegateRight( + const T1&, JsonUInt, + typename enable_if::value>::type* = 0) { + return COMPARE_RESULT_GREATER; +} +template +CompareResult arithmeticCompareNegateRight( + const T1& lhs, JsonUInt rhs, + typename enable_if::value>::type* = 0) { + if (lhs > 0) + return COMPARE_RESULT_GREATER; + return arithmeticCompare(static_cast(rhs), -lhs); +} +struct VariantTag {}; +template +struct IsVariant : is_base_of {}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonVariantConst; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +CompareResult compare(JsonVariantConst lhs, + const T& rhs); // VariantCompare.cpp +struct VariantOperatorTag {}; +template +struct VariantOperators : VariantOperatorTag { + template + friend + typename enable_if::value && !is_array::value, T>::type + operator|(const TVariant& variant, const T& defaultValue) { + if (variant.template is()) + return variant.template as(); + else + return defaultValue; + } + friend const char* operator|(const TVariant& variant, + const char* defaultValue) { + if (variant.template is()) + return variant.template as(); + else + return defaultValue; + } + template + friend typename enable_if::value, JsonVariantConst>::type + operator|(const TVariant& variant, T defaultValue) { + if (variant) + return variant; + else + return defaultValue; + } + template + friend bool operator==(T* lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator==(const T& lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator==(TVariant lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template + friend + typename enable_if::value, bool>::type + operator==(TVariant lhs, const T& rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(T* lhs, TVariant rhs) { + return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(const T& lhs, TVariant rhs) { + return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator!=(TVariant lhs, T* rhs) { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template + friend + typename enable_if::value, bool>::type + operator!=(TVariant lhs, const T& rhs) { + return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; + } + template + friend bool operator<(T* lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator<(const T& lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator<(TVariant lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template + friend + typename enable_if::value, bool>::type + operator<(TVariant lhs, const T& rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator<=(T* lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend bool operator<=(const T& lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend bool operator<=(TVariant lhs, T* rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend + typename enable_if::value, bool>::type + operator<=(TVariant lhs, const T& rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>(T* lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator>(const T& lhs, TVariant rhs) { + return compare(rhs, lhs) == COMPARE_RESULT_LESS; + } + template + friend bool operator>(TVariant lhs, T* rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template + friend + typename enable_if::value, bool>::type + operator>(TVariant lhs, const T& rhs) { + return compare(lhs, rhs) == COMPARE_RESULT_GREATER; + } + template + friend bool operator>=(T* lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>=(const T& lhs, TVariant rhs) { + return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; + } + template + friend bool operator>=(TVariant lhs, T* rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } + template + friend + typename enable_if::value, bool>::type + operator>=(TVariant lhs, const T& rhs) { + return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; + } +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonArray; +class JsonObject; +class JsonVariantConst : public detail::VariantTag, + public detail::VariantOperators { + friend class detail::VariantAttorney; + public: + JsonVariantConst() : data_(0) {} + explicit JsonVariantConst(const detail::VariantData* data) : data_(data) {} + FORCE_INLINE bool isNull() const { + using namespace detail; + return variantIsNull(data_); + } + FORCE_INLINE bool isUnbound() const { + return !data_; + } + FORCE_INLINE size_t memoryUsage() const { + return data_ ? data_->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(data_); + } + size_t size() const { + return variantSize(data_); + } + template + FORCE_INLINE typename detail::enable_if::value && + !detail::is_same::value, + T>::type + as() const { + return Converter::fromJson(*this); + } + template + FORCE_INLINE typename detail::enable_if::value && + !detail::is_same::value, + bool>::type + is() const { + return Converter::checkJson(*this); + } + template + FORCE_INLINE operator T() const { + return as(); + } + FORCE_INLINE JsonVariantConst operator[](size_t index) const { + return JsonVariantConst(variantGetElement(data_, index)); + } + template + FORCE_INLINE typename detail::enable_if::value, + JsonVariantConst>::type + operator[](const TString& key) const { + return JsonVariantConst(variantGetMember(data_, detail::adaptString(key))); + } + template + FORCE_INLINE typename detail::enable_if::value, + JsonVariantConst>::type + operator[](TChar* key) const { + return JsonVariantConst(variantGetMember(data_, detail::adaptString(key))); + } + template + FORCE_INLINE + typename detail::enable_if::value, bool>::type + containsKey(const TString& key) const { + return variantGetMember(getData(), detail::adaptString(key)) != 0; + } + template + FORCE_INLINE + typename detail::enable_if::value, bool>::type + containsKey(TChar* key) const { + return variantGetMember(getData(), detail::adaptString(key)) != 0; + } + protected: + const detail::VariantData* getData() const { + return data_; + } + private: + const detail::VariantData* data_; +}; +class JsonVariant; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class ElementProxy; +template +class MemberProxy; +template +class VariantRefBase : public VariantTag { + friend class VariantAttorney; + public: + FORCE_INLINE void clear() const { + variantSetNull(getData()); + } + FORCE_INLINE bool isNull() const { + return variantIsNull(getData()); + } + FORCE_INLINE bool isUnbound() const { + return !getData(); + } + template + FORCE_INLINE + typename enable_if::value, T>::type + as() const { + return Converter::fromJson(getVariantConst()); + } + template + FORCE_INLINE typename enable_if::value, T>::type + as() const { + return Converter::fromJson(getVariant()); + } + template + FORCE_INLINE operator T() const { + return as(); + } + template + typename enable_if::value, JsonArray>::type to() const; + template + typename enable_if::value, JsonObject>::type to() + const; + template + typename enable_if::value, JsonVariant>::type to() + const; + template + FORCE_INLINE + typename enable_if::value, bool>::type + is() const { + return Converter::checkJson(getVariant()); + } + template + FORCE_INLINE typename enable_if::value && + !is_same::value && + !is_same::value, + bool>::type + is() const { + return Converter::checkJson(getVariantConst()); + } + FORCE_INLINE void shallowCopy(ArduinoJson::JsonVariantConst target) { + VariantData* data = getOrCreateData(); + if (!data) + return; + const VariantData* targetData = VariantAttorney::getData(target); + if (targetData) + *data = *targetData; + else + data->setNull(); + } + template + FORCE_INLINE bool set(const T& value) const { + Converter::toJson(value, getOrCreateVariant()); + MemoryPool* pool = getPool(); + return pool && !pool->overflowed(); + } + template + FORCE_INLINE bool set(T* value) const { + Converter::toJson(value, getOrCreateVariant()); + MemoryPool* pool = getPool(); + return pool && !pool->overflowed(); + } + FORCE_INLINE size_t size() const { + return variantSize(getData()); + } + FORCE_INLINE size_t memoryUsage() const { + VariantData* data = getData(); + return data ? data->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(getData()); + } + FORCE_INLINE JsonVariant add() const; + template + FORCE_INLINE bool add(const T& value) const { + return add().set(value); + } + template + FORCE_INLINE bool add(T* value) const { + return add().set(value); + } + FORCE_INLINE void remove(size_t index) const { + VariantData* data = getData(); + if (data) + data->remove(index); + } + template + FORCE_INLINE typename enable_if::value>::type remove( + TChar* key) const { + VariantData* data = getData(); + if (data) + data->remove(adaptString(key)); + } + template + FORCE_INLINE typename enable_if::value>::type remove( + const TString& key) const { + VariantData* data = getData(); + if (data) + data->remove(adaptString(key)); + } + FORCE_INLINE JsonArray createNestedArray() const; + FORCE_INLINE JsonObject createNestedObject() const; + FORCE_INLINE ElementProxy operator[](size_t index) const; + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(const TString& key) const; + template + FORCE_INLINE typename enable_if::value, bool>::type + containsKey(TChar* key) const; + template + FORCE_INLINE typename enable_if::value, + MemberProxy>::type + operator[](const TString& key) const; + template + FORCE_INLINE typename enable_if::value, + MemberProxy>::type + operator[](TChar* key) const; + template + FORCE_INLINE JsonArray createNestedArray(const TString& key) const; + template + FORCE_INLINE JsonArray createNestedArray(TChar* key) const; + template + JsonObject createNestedObject(const TString& key) const; + template + JsonObject createNestedObject(TChar* key) const; + private: + TDerived& derived() { + return static_cast(*this); + } + const TDerived& derived() const { + return static_cast(*this); + } + FORCE_INLINE MemoryPool* getPool() const { + return VariantAttorney::getPool(derived()); + } + FORCE_INLINE VariantData* getData() const { + return VariantAttorney::getData(derived()); + } + FORCE_INLINE VariantData* getOrCreateData() const { + return VariantAttorney::getOrCreateData(derived()); + } + private: + FORCE_INLINE ArduinoJson::JsonVariant getVariant() const; + FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const { + return ArduinoJson::JsonVariantConst(getData()); + } + FORCE_INLINE ArduinoJson::JsonVariant getOrCreateVariant() const; +}; +template +class ElementProxy : public VariantRefBase>, + public VariantOperators> { + friend class VariantAttorney; + public: + ElementProxy(TUpstream upstream, size_t index) + : upstream_(upstream), index_(index) {} + ElementProxy(const ElementProxy& src) + : upstream_(src.upstream_), index_(src.index_) {} + FORCE_INLINE ElementProxy& operator=(const ElementProxy& src) { + this->set(src); + return *this; + } + template + FORCE_INLINE ElementProxy& operator=(const T& src) { + this->set(src); + return *this; + } + template + FORCE_INLINE ElementProxy& operator=(T* src) { + this->set(src); + return *this; + } + private: + FORCE_INLINE MemoryPool* getPool() const { + return VariantAttorney::getPool(upstream_); + } + FORCE_INLINE VariantData* getData() const { + return variantGetElement(VariantAttorney::getData(upstream_), index_); + } + FORCE_INLINE VariantData* getOrCreateData() const { + return variantGetOrAddElement(VariantAttorney::getOrCreateData(upstream_), + index_, VariantAttorney::getPool(upstream_)); + } + TUpstream upstream_; + size_t index_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonVariant : public detail::VariantRefBase, + public detail::VariantOperators { + friend class detail::VariantAttorney; + public: + JsonVariant() : data_(0), pool_(0) {} + JsonVariant(detail::MemoryPool* pool, detail::VariantData* data) + : data_(data), pool_(pool) {} + private: + FORCE_INLINE detail::MemoryPool* getPool() const { + return pool_; + } + FORCE_INLINE detail::VariantData* getData() const { + return data_; + } + FORCE_INLINE detail::VariantData* getOrCreateData() const { + return data_; + } + detail::VariantData* data_; + detail::MemoryPool* pool_; +}; +template <> +struct Converter : private detail::VariantAttorney { + static void toJson(JsonVariant src, JsonVariant dst) { + detail::variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonVariant fromJson(JsonVariant src) { + return src; + } + static detail::InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariant src) { + auto data = getData(src); + return !!data; + } + static bool checkJson(JsonVariantConst) { + return false; + } +}; +template <> +struct Converter : private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonVariantConst fromJson(JsonVariantConst src) { + return JsonVariantConst(getData(src)); + } + static bool checkJson(JsonVariantConst src) { + auto data = getData(src); + return !!data; + } +}; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +struct SlotKeySetter { + SlotKeySetter(VariantSlot* instance) : instance_(instance) {} + template + void operator()(TStoredString s) { + if (!s) + return; + ARDUINOJSON_ASSERT(instance_ != 0); + instance_->setKey(s); + } + VariantSlot* instance_; +}; +template +inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool) { + if (!var) + return false; + return storeString(pool, key, SlotKeySetter(var)); +} +inline size_t slotSize(const VariantSlot* var) { + size_t n = 0; + while (var) { + n++; + var = var->next(); + } + return n; +} +inline VariantData* slotData(VariantSlot* slot) { + return reinterpret_cast(slot); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class VariantPtr { + public: + VariantPtr(detail::MemoryPool* pool, detail::VariantData* data) + : variant_(pool, data) {} + JsonVariant* operator->() { + return &variant_; + } + JsonVariant& operator*() { + return variant_; + } + private: + JsonVariant variant_; +}; +class JsonArrayIterator { + friend class JsonArray; + public: + JsonArrayIterator() : slot_(0) {} + explicit JsonArrayIterator(detail::MemoryPool* pool, + detail::VariantSlot* slot) + : pool_(pool), slot_(slot) {} + JsonVariant operator*() const { + return JsonVariant(pool_, slot_->data()); + } + VariantPtr operator->() { + return VariantPtr(pool_, slot_->data()); + } + bool operator==(const JsonArrayIterator& other) const { + return slot_ == other.slot_; + } + bool operator!=(const JsonArrayIterator& other) const { + return slot_ != other.slot_; + } + JsonArrayIterator& operator++() { + slot_ = slot_->next(); + return *this; + } + JsonArrayIterator& operator+=(size_t distance) { + slot_ = slot_->next(distance); + return *this; + } + private: + detail::MemoryPool* pool_; + detail::VariantSlot* slot_; +}; +class VariantConstPtr { + public: + VariantConstPtr(const detail::VariantData* data) : variant_(data) {} + JsonVariantConst* operator->() { + return &variant_; + } + JsonVariantConst& operator*() { + return variant_; + } + private: + JsonVariantConst variant_; +}; +class JsonArrayConstIterator { + friend class JsonArray; + public: + JsonArrayConstIterator() : slot_(0) {} + explicit JsonArrayConstIterator(const detail::VariantSlot* slot) + : slot_(slot) {} + JsonVariantConst operator*() const { + return JsonVariantConst(slot_->data()); + } + VariantConstPtr operator->() { + return VariantConstPtr(slot_->data()); + } + bool operator==(const JsonArrayConstIterator& other) const { + return slot_ == other.slot_; + } + bool operator!=(const JsonArrayConstIterator& other) const { + return slot_ != other.slot_; + } + JsonArrayConstIterator& operator++() { + slot_ = slot_->next(); + return *this; + } + JsonArrayConstIterator& operator+=(size_t distance) { + slot_ = slot_->next(distance); + return *this; + } + private: + const detail::VariantSlot* slot_; +}; +class JsonObject; +class JsonArrayConst : public detail::VariantOperators { + friend class JsonArray; + friend class detail::VariantAttorney; + public: + typedef JsonArrayConstIterator iterator; + FORCE_INLINE iterator begin() const { + if (!data_) + return iterator(); + return iterator(data_->head()); + } + FORCE_INLINE iterator end() const { + return iterator(); + } + FORCE_INLINE JsonArrayConst() : data_(0) {} + FORCE_INLINE JsonArrayConst(const detail::CollectionData* data) + : data_(data) {} + FORCE_INLINE bool operator==(JsonArrayConst rhs) const { + if (data_ == rhs.data_) + return true; + if (!data_ || !rhs.data_) + return false; + iterator it1 = begin(); + iterator it2 = rhs.begin(); + for (;;) { + bool end1 = it1 == end(); + bool end2 = it2 == rhs.end(); + if (end1 && end2) + return true; + if (end1 || end2) + return false; + if (*it1 != *it2) + return false; + ++it1; + ++it2; + } + } + FORCE_INLINE JsonVariantConst operator[](size_t index) const { + return JsonVariantConst(data_ ? data_->getElement(index) : 0); + } + operator JsonVariantConst() const { + return JsonVariantConst(collectionToVariant(data_)); + } + FORCE_INLINE bool isNull() const { + return data_ == 0; + } + FORCE_INLINE operator bool() const { + return data_ != 0; + } + FORCE_INLINE size_t memoryUsage() const { + return data_ ? data_->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(collectionToVariant(data_)); + } + FORCE_INLINE size_t size() const { + return data_ ? data_->size() : 0; + } + private: + const detail::VariantData* getData() const { + return collectionToVariant(data_); + } + const detail::CollectionData* data_; +}; +template <> +struct Converter : private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonArrayConst fromJson(JsonVariantConst src) { + auto data = getData(src); + return data ? data->asArray() : 0; + } + static bool checkJson(JsonVariantConst src) { + auto data = getData(src); + return data && data->isArray(); + } +}; +class JsonObject; +class JsonArray : public detail::VariantOperators { + friend class detail::VariantAttorney; + public: + typedef JsonArrayIterator iterator; + FORCE_INLINE JsonArray() : data_(0), pool_(0) {} + FORCE_INLINE JsonArray(detail::MemoryPool* pool, detail::CollectionData* data) + : data_(data), pool_(pool) {} + operator JsonVariant() { + void* data = data_; // prevent warning cast-align + return JsonVariant(pool_, reinterpret_cast(data)); + } + operator JsonArrayConst() const { + return JsonArrayConst(data_); + } + JsonVariant add() const { + if (!data_) + return JsonVariant(); + return JsonVariant(pool_, data_->addElement(pool_)); + } + template + FORCE_INLINE bool add(const T& value) const { + return add().set(value); + } + template + FORCE_INLINE bool add(T* value) const { + return add().set(value); + } + FORCE_INLINE iterator begin() const { + if (!data_) + return iterator(); + return iterator(pool_, data_->head()); + } + FORCE_INLINE iterator end() const { + return iterator(); + } + FORCE_INLINE bool set(JsonArrayConst src) const { + if (!data_ || !src.data_) + return false; + return data_->copyFrom(*src.data_, pool_); + } + FORCE_INLINE bool operator==(JsonArray rhs) const { + return JsonArrayConst(data_) == JsonArrayConst(rhs.data_); + } + FORCE_INLINE void remove(iterator it) const { + if (!data_) + return; + data_->removeSlot(it.slot_); + } + FORCE_INLINE void remove(size_t index) const { + if (!data_) + return; + data_->removeElement(index); + } + void clear() const { + if (!data_) + return; + data_->clear(); + } + FORCE_INLINE detail::ElementProxy operator[](size_t index) const { + return {*this, index}; + } + FORCE_INLINE JsonObject createNestedObject() const; + FORCE_INLINE JsonArray createNestedArray() const { + return add().to(); + } + operator JsonVariantConst() const { + return JsonVariantConst(collectionToVariant(data_)); + } + FORCE_INLINE bool isNull() const { + return data_ == 0; + } + FORCE_INLINE operator bool() const { + return data_ != 0; + } + FORCE_INLINE size_t memoryUsage() const { + return data_ ? data_->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(collectionToVariant(data_)); + } + FORCE_INLINE size_t size() const { + return data_ ? data_->size() : 0; + } + private: + detail::MemoryPool* getPool() const { + return pool_; + } + detail::VariantData* getData() const { + return collectionToVariant(data_); + } + detail::VariantData* getOrCreateData() const { + return collectionToVariant(data_); + } + detail::CollectionData* data_; + detail::MemoryPool* pool_; +}; +template <> +struct Converter : private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonArray fromJson(JsonVariant src) { + auto data = getData(src); + auto pool = getPool(src); + return JsonArray(pool, data != 0 ? data->asArray() : 0); + } + static detail::InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariantConst) { + return false; + } + static bool checkJson(JsonVariant src) { + auto data = getData(src); + return data && data->isArray(); + } +}; +class JsonPair { + public: + JsonPair(detail::MemoryPool* pool, detail::VariantSlot* slot) { + if (slot) { + key_ = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied + : JsonString::Linked); + value_ = JsonVariant(pool, slot->data()); + } + } + JsonString key() const { + return key_; + } + JsonVariant value() const { + return value_; + } + private: + JsonString key_; + JsonVariant value_; +}; +class JsonPairConst { + public: + JsonPairConst(const detail::VariantSlot* slot) { + if (slot) { + key_ = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied + : JsonString::Linked); + value_ = JsonVariantConst(slot->data()); + } + } + JsonString key() const { + return key_; + } + JsonVariantConst value() const { + return value_; + } + private: + JsonString key_; + JsonVariantConst value_; +}; +class JsonPairPtr { + public: + JsonPairPtr(detail::MemoryPool* pool, detail::VariantSlot* slot) + : pair_(pool, slot) {} + const JsonPair* operator->() const { + return &pair_; + } + const JsonPair& operator*() const { + return pair_; + } + private: + JsonPair pair_; +}; +class JsonObjectIterator { + friend class JsonObject; + public: + JsonObjectIterator() : slot_(0) {} + explicit JsonObjectIterator(detail::MemoryPool* pool, + detail::VariantSlot* slot) + : pool_(pool), slot_(slot) {} + JsonPair operator*() const { + return JsonPair(pool_, slot_); + } + JsonPairPtr operator->() { + return JsonPairPtr(pool_, slot_); + } + bool operator==(const JsonObjectIterator& other) const { + return slot_ == other.slot_; + } + bool operator!=(const JsonObjectIterator& other) const { + return slot_ != other.slot_; + } + JsonObjectIterator& operator++() { + slot_ = slot_->next(); + return *this; + } + JsonObjectIterator& operator+=(size_t distance) { + slot_ = slot_->next(distance); + return *this; + } + private: + detail::MemoryPool* pool_; + detail::VariantSlot* slot_; +}; +class JsonPairConstPtr { + public: + JsonPairConstPtr(const detail::VariantSlot* slot) : pair_(slot) {} + const JsonPairConst* operator->() const { + return &pair_; + } + const JsonPairConst& operator*() const { + return pair_; + } + private: + JsonPairConst pair_; +}; +class JsonObjectConstIterator { + friend class JsonObject; + public: + JsonObjectConstIterator() : slot_(0) {} + explicit JsonObjectConstIterator(const detail::VariantSlot* slot) + : slot_(slot) {} + JsonPairConst operator*() const { + return JsonPairConst(slot_); + } + JsonPairConstPtr operator->() { + return JsonPairConstPtr(slot_); + } + bool operator==(const JsonObjectConstIterator& other) const { + return slot_ == other.slot_; + } + bool operator!=(const JsonObjectConstIterator& other) const { + return slot_ != other.slot_; + } + JsonObjectConstIterator& operator++() { + slot_ = slot_->next(); + return *this; + } + JsonObjectConstIterator& operator+=(size_t distance) { + slot_ = slot_->next(distance); + return *this; + } + private: + const detail::VariantSlot* slot_; +}; +class JsonObjectConst : public detail::VariantOperators { + friend class JsonObject; + friend class detail::VariantAttorney; + public: + typedef JsonObjectConstIterator iterator; + JsonObjectConst() : data_(0) {} + JsonObjectConst(const detail::CollectionData* data) : data_(data) {} + operator JsonVariantConst() const { + return JsonVariantConst(collectionToVariant(data_)); + } + FORCE_INLINE bool isNull() const { + return data_ == 0; + } + FORCE_INLINE operator bool() const { + return data_ != 0; + } + FORCE_INLINE size_t memoryUsage() const { + return data_ ? data_->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(collectionToVariant(data_)); + } + FORCE_INLINE size_t size() const { + return data_ ? data_->size() : 0; + } + FORCE_INLINE iterator begin() const { + if (!data_) + return iterator(); + return iterator(data_->head()); + } + FORCE_INLINE iterator end() const { + return iterator(); + } + template + FORCE_INLINE bool containsKey(const TString& key) const { + return getMember(detail::adaptString(key)) != 0; + } + template + FORCE_INLINE bool containsKey(TChar* key) const { + return getMember(detail::adaptString(key)) != 0; + } + template + FORCE_INLINE typename detail::enable_if::value, + JsonVariantConst>::type + operator[](const TString& key) const { + return JsonVariantConst(getMember(detail::adaptString(key))); + } + template + FORCE_INLINE typename detail::enable_if::value, + JsonVariantConst>::type + operator[](TChar* key) const { + return JsonVariantConst(getMember(detail::adaptString(key))); + } + FORCE_INLINE bool operator==(JsonObjectConst rhs) const { + if (data_ == rhs.data_) + return true; + if (!data_ || !rhs.data_) + return false; + size_t count = 0; + for (iterator it = begin(); it != end(); ++it) { + if (it->value() != rhs[it->key()]) + return false; + count++; + } + return count == rhs.size(); + } + private: + const detail::VariantData* getData() const { + return collectionToVariant(data_); + } + template + const detail::VariantData* getMember(TAdaptedString key) const { + if (!data_) + return 0; + return data_->getMember(key); + } + const detail::CollectionData* data_; +}; +template <> +struct Converter : private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonObjectConst fromJson(JsonVariantConst src) { + auto data = getData(src); + return data != 0 ? data->asObject() : 0; + } + static bool checkJson(JsonVariantConst src) { + auto data = getData(src); + return data && data->isObject(); + } +}; +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +class MemberProxy + : public VariantRefBase>, + public VariantOperators> { + friend class VariantAttorney; + public: + FORCE_INLINE MemberProxy(TUpstream upstream, TStringRef key) + : upstream_(upstream), key_(key) {} + MemberProxy(const MemberProxy& src) + : upstream_(src.upstream_), key_(src.key_) {} + FORCE_INLINE MemberProxy& operator=(const MemberProxy& src) { + this->set(src); + return *this; + } + template + FORCE_INLINE MemberProxy& operator=(const T& src) { + this->set(src); + return *this; + } + template + FORCE_INLINE MemberProxy& operator=(T* src) { + this->set(src); + return *this; + } + private: + FORCE_INLINE MemoryPool* getPool() const { + return VariantAttorney::getPool(upstream_); + } + FORCE_INLINE VariantData* getData() const { + return variantGetMember(VariantAttorney::getData(upstream_), + adaptString(key_)); + } + FORCE_INLINE VariantData* getOrCreateData() const { + return variantGetOrAddMember(VariantAttorney::getOrCreateData(upstream_), + adaptString(key_), + VariantAttorney::getPool(upstream_)); + } + private: + TUpstream upstream_; + TStringRef key_; +}; +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +class JsonArray; +class JsonObject : public detail::VariantOperators { + friend class detail::VariantAttorney; + public: + typedef JsonObjectIterator iterator; + FORCE_INLINE JsonObject() : data_(0), pool_(0) {} + FORCE_INLINE JsonObject(detail::MemoryPool* buf, detail::CollectionData* data) + : data_(data), pool_(buf) {} + operator JsonVariant() const { + void* data = data_; // prevent warning cast-align + return JsonVariant(pool_, reinterpret_cast(data)); + } + operator JsonObjectConst() const { + return JsonObjectConst(data_); + } + operator JsonVariantConst() const { + return JsonVariantConst(collectionToVariant(data_)); + } + FORCE_INLINE bool isNull() const { + return data_ == 0; + } + FORCE_INLINE operator bool() const { + return data_ != 0; + } + FORCE_INLINE size_t memoryUsage() const { + return data_ ? data_->memoryUsage() : 0; + } + FORCE_INLINE size_t nesting() const { + return variantNesting(collectionToVariant(data_)); + } + FORCE_INLINE size_t size() const { + return data_ ? data_->size() : 0; + } + FORCE_INLINE iterator begin() const { + if (!data_) + return iterator(); + return iterator(pool_, data_->head()); + } + FORCE_INLINE iterator end() const { + return iterator(); + } + void clear() const { + if (!data_) + return; + data_->clear(); + } + FORCE_INLINE bool set(JsonObjectConst src) { + if (!data_ || !src.data_) + return false; + return data_->copyFrom(*src.data_, pool_); + } + FORCE_INLINE bool operator==(JsonObject rhs) const { + return JsonObjectConst(data_) == JsonObjectConst(rhs.data_); + } + template + FORCE_INLINE + typename detail::enable_if::value, + detail::MemberProxy>::type + operator[](const TString& key) const { + return {*this, key}; + } + template + FORCE_INLINE + typename detail::enable_if::value, + detail::MemberProxy>::type + operator[](TChar* key) const { + return {*this, key}; + } + FORCE_INLINE void remove(iterator it) const { + if (!data_) + return; + data_->removeSlot(it.slot_); + } + template + FORCE_INLINE void remove(const TString& key) const { + removeMember(detail::adaptString(key)); + } + template + FORCE_INLINE void remove(TChar* key) const { + removeMember(detail::adaptString(key)); + } + template + FORCE_INLINE + typename detail::enable_if::value, bool>::type + containsKey(const TString& key) const { + return getMember(detail::adaptString(key)) != 0; + } + template + FORCE_INLINE + typename detail::enable_if::value, bool>::type + containsKey(TChar* key) const { + return getMember(detail::adaptString(key)) != 0; + } + template + FORCE_INLINE JsonArray createNestedArray(const TString& key) const; + template + FORCE_INLINE JsonArray createNestedArray(TChar* key) const; + template + JsonObject createNestedObject(const TString& key) const { + return operator[](key).template to(); + } + template + JsonObject createNestedObject(TChar* key) const { + return operator[](key).template to(); + } + private: + detail::MemoryPool* getPool() const { + return pool_; + } + detail::VariantData* getData() const { + return detail::collectionToVariant(data_); + } + detail::VariantData* getOrCreateData() const { + return detail::collectionToVariant(data_); + } + template + inline detail::VariantData* getMember(TAdaptedString key) const { + if (!data_) + return 0; + return data_->getMember(key); + } + template + void removeMember(TAdaptedString key) const { + if (!data_) + return; + data_->removeMember(key); + } + detail::CollectionData* data_; + detail::MemoryPool* pool_; +}; +template <> +struct Converter : private detail::VariantAttorney { + static void toJson(JsonVariantConst src, JsonVariant dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); + } + static JsonObject fromJson(JsonVariant src) { + auto data = getData(src); + auto pool = getPool(src); + return JsonObject(pool, data != 0 ? data->asObject() : 0); + } + static detail::InvalidConversion fromJson( + JsonVariantConst); + static bool checkJson(JsonVariantConst) { + return false; + } + static bool checkJson(JsonVariant src) { + auto data = getData(src); + return data && data->isObject(); + } +}; +class JsonDocument : public detail::VariantOperators { + friend class detail::VariantAttorney; + public: + JsonDocument(const JsonDocument&) = delete; + JsonDocument& operator=(const JsonDocument&) = delete; + template + T as() { + return getVariant().template as(); + } + template + T as() const { + return getVariant().template as(); + } + void clear() { + pool_.clear(); + data_.setNull(); + } + template + bool is() { + return getVariant().template is(); + } + template + bool is() const { + return getVariant().template is(); + } + bool isNull() const { + return getVariant().isNull(); + } + size_t memoryUsage() const { + return pool_.size(); + } + bool overflowed() const { + return pool_.overflowed(); + } + size_t nesting() const { + return variantNesting(&data_); + } + size_t capacity() const { + return pool_.capacity(); + } + size_t size() const { + return data_.size(); + } + bool set(const JsonDocument& src) { + return to().set(src.as()); + } + template + typename detail::enable_if::value, + bool>::type + set(const T& src) { + return to().set(src); + } + template + typename detail::VariantTo::type to() { + clear(); + return getVariant().template to(); + } + JsonArray createNestedArray() { + return add().to(); + } + template + JsonArray createNestedArray(TChar* key) { + return operator[](key).template to(); + } + template + JsonArray createNestedArray(const TString& key) { + return operator[](key).template to(); + } + JsonObject createNestedObject() { + return add().to(); + } + template + JsonObject createNestedObject(TChar* key) { + return operator[](key).template to(); + } + template + JsonObject createNestedObject(const TString& key) { + return operator[](key).template to(); + } + template + bool containsKey(TChar* key) const { + return data_.getMember(detail::adaptString(key)) != 0; + } + template + bool containsKey(const TString& key) const { + return data_.getMember(detail::adaptString(key)) != 0; + } + template + FORCE_INLINE typename detail::enable_if< + detail::IsString::value, + detail::MemberProxy>::type + operator[](const TString& key) { + return {*this, key}; + } + template + FORCE_INLINE typename detail::enable_if< + detail::IsString::value, + detail::MemberProxy>::type + operator[](TChar* key) { + return {*this, key}; + } + template + FORCE_INLINE typename detail::enable_if::value, + JsonVariantConst>::type + operator[](const TString& key) const { + return JsonVariantConst(data_.getMember(detail::adaptString(key))); + } + template + FORCE_INLINE typename detail::enable_if::value, + JsonVariantConst>::type + operator[](TChar* key) const { + return JsonVariantConst(data_.getMember(detail::adaptString(key))); + } + FORCE_INLINE detail::ElementProxy operator[](size_t index) { + return {*this, index}; + } + FORCE_INLINE JsonVariantConst operator[](size_t index) const { + return JsonVariantConst(data_.getElement(index)); + } + FORCE_INLINE JsonVariant add() { + return JsonVariant(&pool_, data_.addElement(&pool_)); + } + template + FORCE_INLINE bool add(const TValue& value) { + return add().set(value); + } + template + FORCE_INLINE bool add(TChar* value) { + return add().set(value); + } + FORCE_INLINE void remove(size_t index) { + data_.remove(index); + } + template + FORCE_INLINE typename detail::enable_if::value>::type + remove(TChar* key) { + data_.remove(detail::adaptString(key)); + } + template + FORCE_INLINE + typename detail::enable_if::value>::type + remove(const TString& key) { + data_.remove(detail::adaptString(key)); + } + FORCE_INLINE operator JsonVariant() { + return getVariant(); + } + FORCE_INLINE operator JsonVariantConst() const { + return getVariant(); + } + protected: + JsonDocument() : pool_(0, 0) {} + JsonDocument(detail::MemoryPool pool) : pool_(pool) {} + JsonDocument(char* buf, size_t capa) : pool_(buf, capa) {} + ~JsonDocument() {} + void replacePool(detail::MemoryPool pool) { + pool_ = pool; + } + JsonVariant getVariant() { + return JsonVariant(&pool_, &data_); + } + JsonVariantConst getVariant() const { + return JsonVariantConst(&data_); + } + detail::MemoryPool pool_; + detail::VariantData data_; + protected: + detail::MemoryPool* getPool() { + return &pool_; + } + detail::VariantData* getData() { + return &data_; + } + const detail::VariantData* getData() const { + return &data_; + } + detail::VariantData* getOrCreateData() { + return &data_; + } +}; +inline void convertToJson(const JsonDocument& src, JsonVariant dst) { + dst.set(src.as()); +} +template +class AllocatorOwner { + public: + AllocatorOwner() {} + AllocatorOwner(TAllocator a) : allocator_(a) {} + void* allocate(size_t size) { + return allocator_.allocate(size); + } + void deallocate(void* ptr) { + if (ptr) + allocator_.deallocate(ptr); + } + void* reallocate(void* ptr, size_t new_size) { + return allocator_.reallocate(ptr, new_size); + } + TAllocator& allocator() { + return allocator_; + } + private: + TAllocator allocator_; +}; +template +class BasicJsonDocument : AllocatorOwner, public JsonDocument { + public: + explicit BasicJsonDocument(size_t capa, TAllocator alloc = TAllocator()) + : AllocatorOwner(alloc), JsonDocument(allocPool(capa)) {} + BasicJsonDocument(const BasicJsonDocument& src) + : AllocatorOwner(src), JsonDocument() { + copyAssignFrom(src); + } + BasicJsonDocument(BasicJsonDocument&& src) : AllocatorOwner(src) { + moveAssignFrom(src); + } + BasicJsonDocument(const JsonDocument& src) { + copyAssignFrom(src); + } + template + BasicJsonDocument(const T& src, + typename detail::enable_if< + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value || + detail::is_same::value>::type* = 0) + : JsonDocument(allocPool(src.memoryUsage())) { + set(src); + } + BasicJsonDocument(JsonVariant src) + : JsonDocument(allocPool(src.memoryUsage())) { + set(src); + } + ~BasicJsonDocument() { + freePool(); + } + BasicJsonDocument& operator=(const BasicJsonDocument& src) { + copyAssignFrom(src); + return *this; + } + BasicJsonDocument& operator=(BasicJsonDocument&& src) { + moveAssignFrom(src); + return *this; + } + template + BasicJsonDocument& operator=(const T& src) { + size_t requiredSize = src.memoryUsage(); + if (requiredSize > capacity()) + reallocPool(requiredSize); + set(src); + return *this; + } + void shrinkToFit() { + ptrdiff_t bytes_reclaimed = pool_.squash(); + if (bytes_reclaimed == 0) + return; + void* old_ptr = pool_.buffer(); + void* new_ptr = this->reallocate(old_ptr, pool_.capacity()); + ptrdiff_t ptr_offset = + static_cast(new_ptr) - static_cast(old_ptr); + pool_.movePointers(ptr_offset); + data_.movePointers(ptr_offset, ptr_offset - bytes_reclaimed); + } + bool garbageCollect() { + BasicJsonDocument tmp(*this); + if (!tmp.capacity()) + return false; + moveAssignFrom(tmp); + return true; + } + using AllocatorOwner::allocator; + private: + detail::MemoryPool allocPool(size_t requiredSize) { + size_t capa = detail::addPadding(requiredSize); + return {reinterpret_cast(this->allocate(capa)), capa}; + } + void reallocPool(size_t requiredSize) { + size_t capa = detail::addPadding(requiredSize); + if (capa == pool_.capacity()) + return; + freePool(); + replacePool(allocPool(detail::addPadding(requiredSize))); + } + void freePool() { + this->deallocate(getPool()->buffer()); + } + void copyAssignFrom(const JsonDocument& src) { + reallocPool(src.capacity()); + set(src); + } + void moveAssignFrom(BasicJsonDocument& src) { + freePool(); + data_ = src.data_; + pool_ = src.pool_; + src.data_.setNull(); + src.pool_ = {0, 0}; + } +}; +struct DefaultAllocator { + void* allocate(size_t size) { + return malloc(size); + } + void deallocate(void* ptr) { + free(ptr); + } + void* reallocate(void* ptr, size_t new_size) { + return realloc(ptr, new_size); + } +}; +typedef BasicJsonDocument DynamicJsonDocument; +template +class StaticJsonDocument : public JsonDocument { + static const size_t capacity_ = + detail::AddPadding::value>::value; + public: + StaticJsonDocument() : JsonDocument(buffer_, capacity_) {} + StaticJsonDocument(const StaticJsonDocument& src) + : JsonDocument(buffer_, capacity_) { + set(src); + } + template + StaticJsonDocument( + const T& src, + typename detail::enable_if< + detail::is_convertible::value>::type* = 0) + : JsonDocument(buffer_, capacity_) { + set(src); + } + StaticJsonDocument(JsonVariant src) : JsonDocument(buffer_, capacity_) { + set(src); + } + StaticJsonDocument& operator=(const StaticJsonDocument& src) { + set(src); + return *this; + } + template + StaticJsonDocument& operator=(const T& src) { + set(src); + return *this; + } + void garbageCollect() { + StaticJsonDocument tmp(*this); + set(tmp); + } + private: + char buffer_[capacity_]; +}; +inline JsonObject JsonArray::createNestedObject() const { + return add().to(); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +inline JsonArray VariantRefBase::createNestedArray() const { + return add().template to(); +} +template +inline JsonObject VariantRefBase::createNestedObject() const { + return add().template to(); +} +template +inline ElementProxy VariantRefBase::operator[]( + size_t index) const { + return ElementProxy(derived(), index); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +inline typename detail::enable_if::value, bool>::type +copyArray(const T& src, JsonVariant dst) { + return dst.set(src); +} +template +inline typename detail::enable_if< + !detail::is_base_of::value, bool>::type +copyArray(T (&src)[N], const TDestination& dst) { + return copyArray(src, N, dst); +} +template +inline typename detail::enable_if< + !detail::is_base_of::value, bool>::type +copyArray(const T* src, size_t len, const TDestination& dst) { + bool ok = true; + for (size_t i = 0; i < len; i++) { + ok &= copyArray(src[i], dst.add()); + } + return ok; +} +template +inline bool copyArray(const char* src, size_t, const TDestination& dst) { + return dst.set(src); +} +template +inline bool copyArray(const T& src, JsonDocument& dst) { + return copyArray(src, dst.to()); +} +template +inline bool copyArray(const T* src, size_t len, JsonDocument& dst) { + return copyArray(src, len, dst.to()); +} +template +inline typename detail::enable_if::value, size_t>::type +copyArray(JsonVariantConst src, T& dst) { + dst = src.as(); + return 1; +} +template +inline size_t copyArray(JsonArrayConst src, T (&dst)[N]) { + return copyArray(src, dst, N); +} +template +inline size_t copyArray(JsonArrayConst src, T* dst, size_t len) { + size_t i = 0; + for (JsonArrayConst::iterator it = src.begin(); it != src.end() && i < len; + ++it) + copyArray(*it, dst[i++]); + return i; +} +template +inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) { + JsonString s = src; + size_t len = N - 1; + if (len > s.size()) + len = s.size(); + memcpy(dst, s.c_str(), len); + dst[len] = 0; + return 1; +} +template +inline typename detail::enable_if< + detail::is_array::value && + detail::is_base_of::value, + size_t>::type +copyArray(const TSource& src, T& dst) { + return copyArray(src.template as(), dst); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) { + VariantSlot* slot = pool->allocVariant(); + if (!slot) + return 0; + if (tail_) { + ARDUINOJSON_ASSERT(pool->owns(tail_)); // Can't alter a linked array/object + tail_->setNextNotNull(slot); + tail_ = slot; + } else { + head_ = slot; + tail_ = slot; + } + slot->clear(); + return slot; +} +inline VariantData* CollectionData::addElement(MemoryPool* pool) { + return slotData(addSlot(pool)); +} +template +inline VariantData* CollectionData::addMember(TAdaptedString key, + MemoryPool* pool) { + VariantSlot* slot = addSlot(pool); + if (!slotSetKey(slot, key, pool)) { + removeSlot(slot); + return 0; + } + return slot->data(); +} +inline void CollectionData::clear() { + head_ = 0; + tail_ = 0; +} +template +inline bool CollectionData::containsKey(const TAdaptedString& key) const { + return getSlot(key) != 0; +} +inline bool CollectionData::copyFrom(const CollectionData& src, + MemoryPool* pool) { + clear(); + for (VariantSlot* s = src.head_; s; s = s->next()) { + VariantData* var; + if (s->key() != 0) { + JsonString key(s->key(), + s->ownsKey() ? JsonString::Copied : JsonString::Linked); + var = addMember(adaptString(key), pool); + } else { + var = addElement(pool); + } + if (!var) + return false; + if (!var->copyFrom(*s->data(), pool)) + return false; + } + return true; +} +template +inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const { + if (key.isNull()) + return 0; + VariantSlot* slot = head_; + while (slot) { + if (stringEquals(key, adaptString(slot->key()))) + break; + slot = slot->next(); + } + return slot; +} +inline VariantSlot* CollectionData::getSlot(size_t index) const { + if (!head_) + return 0; + return head_->next(index); +} +inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const { + VariantSlot* current = head_; + while (current) { + VariantSlot* next = current->next(); + if (next == target) + return current; + current = next; + } + return 0; +} +template +inline VariantData* CollectionData::getMember(TAdaptedString key) const { + VariantSlot* slot = getSlot(key); + return slot ? slot->data() : 0; +} +template +inline VariantData* CollectionData::getOrAddMember(TAdaptedString key, + MemoryPool* pool) { + if (key.isNull()) + return 0; + VariantSlot* slot = getSlot(key); + if (slot) + return slot->data(); + return addMember(key, pool); +} +inline VariantData* CollectionData::getElement(size_t index) const { + VariantSlot* slot = getSlot(index); + return slot ? slot->data() : 0; +} +inline VariantData* CollectionData::getOrAddElement(size_t index, + MemoryPool* pool) { + VariantSlot* slot = head_; + while (slot && index > 0) { + slot = slot->next(); + index--; + } + if (!slot) + index++; + while (index > 0) { + slot = addSlot(pool); + index--; + } + return slotData(slot); +} +inline void CollectionData::removeSlot(VariantSlot* slot) { + if (!slot) + return; + VariantSlot* prev = getPreviousSlot(slot); + VariantSlot* next = slot->next(); + if (prev) + prev->setNext(next); + else + head_ = next; + if (!next) + tail_ = prev; +} +inline void CollectionData::removeElement(size_t index) { + removeSlot(getSlot(index)); +} +inline size_t CollectionData::memoryUsage() const { + size_t total = 0; + for (VariantSlot* s = head_; s; s = s->next()) { + total += sizeof(VariantSlot) + s->data()->memoryUsage(); + if (s->ownsKey()) + total += strlen(s->key()) + 1; + } + return total; +} +inline size_t CollectionData::size() const { + return slotSize(head_); +} +template +inline void movePointer(T*& p, ptrdiff_t offset) { + if (!p) + return; + p = reinterpret_cast( + reinterpret_cast(reinterpret_cast(p) + offset)); + ARDUINOJSON_ASSERT(isAligned(p)); +} +inline void CollectionData::movePointers(ptrdiff_t stringDistance, + ptrdiff_t variantDistance) { + movePointer(head_, variantDistance); + movePointer(tail_, variantDistance); + for (VariantSlot* slot = head_; slot; slot = slot->next()) + slot->movePointers(stringDistance, variantDistance); +} +ARDUINOJSON_END_PRIVATE_NAMESPACE +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE +template +inline JsonArray JsonObject::createNestedArray(const TString& key) const { + return operator[](key).template to(); +} +template +inline JsonArray JsonObject::createNestedArray(TChar* key) const { + return operator[](key).template to(); +} +ARDUINOJSON_END_PUBLIC_NAMESPACE +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE +template +template +inline JsonArray VariantRefBase::createNestedArray( + const TString& key) const { + return operator[](key).template to(); +} +template +template +inline JsonArray VariantRefBase::createNestedArray(TChar* key) const { + return operator[](key).template to(); +} +template +template +inline JsonObject VariantRefBase::createNestedObject( + const TString& key) const { + return operator[](key).template to(); +} +template +template +inline JsonObject VariantRefBase::createNestedObject( + TChar* key) const { + return operator[](key).template to(); +} +template +template +inline typename enable_if::value, bool>::type +VariantRefBase::containsKey(const TString& key) const { + return variantGetMember(VariantAttorney::getData(derived()), + adaptString(key)) != 0; +} +template +template +inline typename enable_if::value, bool>::type +VariantRefBase::containsKey(TChar* key) const { + return variantGetMember(VariantAttorney::getData(derived()), + adaptString(key)) != 0; +} +template +template +inline typename enable_if::value, + MemberProxy>::type +VariantRefBase::operator[](TString* key) const { + return MemberProxy(derived(), key); +} +template +template +inline typename enable_if::value, + MemberProxy>::type +VariantRefBase::operator[](const TString& key) const { + return MemberProxy(derived(), key); +} +class EscapeSequence { + public: + static char escapeChar(char c) { + const char* p = escapeTable(true); + while (p[0] && p[1] != c) { + p += 2; + } + return p[0]; + } + static char unescapeChar(char c) { + const char* p = escapeTable(false); + for (;;) { + if (p[0] == '\0') + return 0; + if (p[0] == c) + return p[1]; + p += 2; + } + } + private: + static const char* escapeTable(bool excludeSolidus) { + return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0]; + } +}; +template +struct FloatParts { + uint32_t integral; + uint32_t decimal; + int16_t exponent; + int8_t decimalPlaces; + FloatParts(TFloat value) { + uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000; + decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6; + exponent = normalize(value); + integral = uint32_t(value); + for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) { + maxDecimalPart /= 10; + decimalPlaces--; + } + TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart); + decimal = uint32_t(remainder); + remainder = remainder - TFloat(decimal); + decimal += uint32_t(remainder * 2); + if (decimal >= maxDecimalPart) { + decimal = 0; + integral++; + if (exponent && integral >= 10) { + exponent++; + integral = 1; + } + } + while (decimal % 10 == 0 && decimalPlaces > 0) { + decimal /= 10; + decimalPlaces--; + } + } + static int16_t normalize(TFloat& value) { + typedef FloatTraits traits; + int16_t powersOf10 = 0; + int8_t index = sizeof(TFloat) == 8 ? 8 : 5; + int bit = 1 << index; + if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value >= traits::positiveBinaryPowersOfTen()[index]) { + value *= traits::negativeBinaryPowersOfTen()[index]; + powersOf10 = int16_t(powersOf10 + bit); + } + bit >>= 1; + } + } + if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value < traits::negativeBinaryPowersOfTen()[index] * 10) { + value *= traits::positiveBinaryPowersOfTen()[index]; + powersOf10 = int16_t(powersOf10 - bit); + } + bit >>= 1; + } + } + return powersOf10; + } +}; +template +class CountingDecorator { + public: + explicit CountingDecorator(TWriter& writer) : writer_(writer), count_(0) {} + void write(uint8_t c) { + count_ += writer_.write(c); + } + void write(const uint8_t* s, size_t n) { + count_ += writer_.write(s, n); + } + size_t count() const { + return count_; + } + private: + TWriter writer_; + size_t count_; +}; +template +class TextFormatter { + public: + explicit TextFormatter(TWriter writer) : writer_(writer) {} + TextFormatter& operator=(const TextFormatter&) = delete; + size_t bytesWritten() const { + return writer_.count(); + } + void writeBoolean(bool value) { + if (value) + writeRaw("true"); + else + writeRaw("false"); + } + void writeString(const char* value) { + ARDUINOJSON_ASSERT(value != NULL); + writeRaw('\"'); + while (*value) + writeChar(*value++); + writeRaw('\"'); + } + void writeString(const char* value, size_t n) { + ARDUINOJSON_ASSERT(value != NULL); + writeRaw('\"'); + while (n--) + writeChar(*value++); + writeRaw('\"'); + } + void writeChar(char c) { + char specialChar = EscapeSequence::escapeChar(c); + if (specialChar) { + writeRaw('\\'); + writeRaw(specialChar); + } else if (c) { + writeRaw(c); + } else { + writeRaw("\\u0000"); + } + } + template + void writeFloat(T value) { + if (isnan(value)) + return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null"); +#if ARDUINOJSON_ENABLE_INFINITY + if (value < 0.0) { + writeRaw('-'); + value = -value; + } + if (isinf(value)) + return writeRaw("Infinity"); +#else + if (isinf(value)) + return writeRaw("null"); + if (value < 0.0) { + writeRaw('-'); + value = -value; + } +#endif + FloatParts parts(value); + writeInteger(parts.integral); + if (parts.decimalPlaces) + writeDecimals(parts.decimal, parts.decimalPlaces); + if (parts.exponent) { + writeRaw('e'); + writeInteger(parts.exponent); + } + } + template + typename enable_if::value>::type writeInteger(T value) { + typedef typename make_unsigned::type unsigned_type; + unsigned_type unsigned_value; + if (value < 0) { + writeRaw('-'); + unsigned_value = unsigned_type(unsigned_type(~value) + 1); + } else { + unsigned_value = unsigned_type(value); + } + writeInteger(unsigned_value); + } + template + typename enable_if::value>::type writeInteger(T value) { + char buffer[22]; + char* end = buffer + sizeof(buffer); + char* begin = end; + do { + *--begin = char(value % 10 + '0'); + value = T(value / 10); + } while (value); + writeRaw(begin, end); + } + void writeDecimals(uint32_t value, int8_t width) { + char buffer[16]; + char* end = buffer + sizeof(buffer); + char* begin = end; + while (width--) { + *--begin = char(value % 10 + '0'); + value /= 10; + } + *--begin = '.'; + writeRaw(begin, end); + } + void writeRaw(const char* s) { + writer_.write(reinterpret_cast(s), strlen(s)); + } + void writeRaw(const char* s, size_t n) { + writer_.write(reinterpret_cast(s), n); + } + void writeRaw(const char* begin, const char* end) { + writer_.write(reinterpret_cast(begin), + static_cast(end - begin)); + } + template + void writeRaw(const char (&s)[N]) { + writer_.write(reinterpret_cast(s), N - 1); + } + void writeRaw(char c) { + writer_.write(static_cast(c)); + } + protected: + CountingDecorator writer_; +}; +class DummyWriter { + public: + size_t write(uint8_t) { + return 1; + } + size_t write(const uint8_t*, size_t n) { + return n; + } +}; +template