Repository: soarqin/D2RMH Branch: master Commit: 32d55b8ab9a3 Files: 210 Total size: 5.0 MB Directory structure: gitextract_68bzuhg8/ ├── .github/ │ └── workflows/ │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bin/ │ ├── D2RMH.ini │ ├── D2RMH_gamedata.ini │ ├── D2RMH_item.ini │ └── plugins/ │ ├── chicken_life.lua │ ├── hotkey.lua │ └── town_portal_check.lua ├── build_msvc2019.bat ├── build_msvc2022.bat ├── build_msys2_clang.bat ├── build_msys2_mingw.bat ├── cmake/ │ ├── CustomCompilerOptions.cmake │ └── GetVersion.cmake ├── contrib/ │ └── zhCN/ │ ├── D2RMH.ini │ ├── README.md │ └── plugins/ │ ├── chicken_life.lua │ └── town_portal_check.lua ├── copy_dist.bat ├── d2mapapi/ │ ├── .gitignore │ ├── .gitrepo │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── cmake/ │ │ └── CustomCompilerOptions.cmake │ ├── collisionmap.cpp │ ├── collisionmap.h │ ├── crc32.h │ ├── d2map.cpp │ ├── d2map.h │ ├── d2ptrs.h │ ├── d2structs.h │ ├── genimage.cpp │ ├── host.cpp │ ├── httpd.cpp │ ├── json/ │ │ ├── LICENSE.MIT │ │ ├── README.md │ │ └── json.hpp │ ├── mapdata.cpp │ ├── mapdata.h │ ├── offset.cpp │ ├── offset.h │ ├── pathfinder.cpp │ ├── pathfinder.h │ ├── piped.cpp │ ├── pipehost.cpp │ ├── pipehost.h │ ├── session.cpp │ ├── session.h │ ├── simphttp/ │ │ ├── CMakeLists.txt │ │ ├── cmake/ │ │ │ └── FindLibUV.cmake │ │ ├── llhttp/ │ │ │ ├── CMakeLists.txt │ │ │ ├── api.c │ │ │ ├── api.h │ │ │ ├── http.c │ │ │ ├── llhttp.c │ │ │ └── llhttp.h │ │ ├── simphttp.cpp │ │ ├── simphttp.h │ │ └── uri.h │ └── stb/ │ └── stb_image_write.h ├── deps/ │ ├── CMakeLists.txt │ ├── CascLib/ │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── README.md │ │ └── src/ │ │ ├── CascCommon.h │ │ ├── CascDecompress.cpp │ │ ├── CascDecrypt.cpp │ │ ├── CascDumpData.cpp │ │ ├── CascFiles.cpp │ │ ├── CascFindFile.cpp │ │ ├── CascIndexFiles.cpp │ │ ├── CascLib.h │ │ ├── CascOpenFile.cpp │ │ ├── CascOpenStorage.cpp │ │ ├── CascPort.h │ │ ├── CascReadFile.cpp │ │ ├── CascRootFile_Diablo3.cpp │ │ ├── CascRootFile_Install.cpp │ │ ├── CascRootFile_MNDX.cpp │ │ ├── CascRootFile_OW.cpp │ │ ├── CascRootFile_TVFS.cpp │ │ ├── CascRootFile_Text.cpp │ │ ├── CascRootFile_WoW.cpp │ │ ├── CascStructs.h │ │ ├── DllMain.c │ │ ├── DllMain.def │ │ ├── DllMain.rc │ │ ├── common/ │ │ │ ├── Array.h │ │ │ ├── Common.cpp │ │ │ ├── Common.h │ │ │ ├── Csv.cpp │ │ │ ├── Csv.h │ │ │ ├── Directory.cpp │ │ │ ├── Directory.h │ │ │ ├── FileStream.cpp │ │ │ ├── FileStream.h │ │ │ ├── FileTree.cpp │ │ │ ├── FileTree.h │ │ │ ├── IndexMap.h │ │ │ ├── ListFile.cpp │ │ │ ├── ListFile.h │ │ │ ├── Map.h │ │ │ ├── Mime.cpp │ │ │ ├── Mime.h │ │ │ ├── Path.h │ │ │ ├── RootHandler.cpp │ │ │ ├── RootHandler.h │ │ │ ├── Sockets.cpp │ │ │ └── Sockets.h │ │ ├── jenkins/ │ │ │ ├── lookup.h │ │ │ └── lookup3.c │ │ ├── md5/ │ │ │ ├── md5.cpp │ │ │ └── md5.h │ │ ├── resource.h │ │ └── zlib/ │ │ ├── adler32.c │ │ ├── crc32.c │ │ ├── crc32.h │ │ ├── deflate.c │ │ ├── deflate.h │ │ ├── gzguts.h │ │ ├── inffast.c │ │ ├── inffast.h │ │ ├── inffixed.h │ │ ├── inflate.c │ │ ├── inflate.h │ │ ├── inftrees.c │ │ ├── inftrees.h │ │ ├── trees.c │ │ ├── trees.h │ │ ├── zconf.h │ │ ├── zlib.h │ │ ├── zutil.c │ │ └── zutil.h │ ├── glad/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ ├── KHR/ │ │ │ │ └── khrplatform.h │ │ │ └── glad/ │ │ │ ├── glad.h │ │ │ └── glad_wgl.h │ │ └── src/ │ │ ├── glad.c │ │ └── glad_wgl.c │ ├── inih/ │ │ ├── CMakeLists.txt │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── ini.c │ │ └── ini.h │ ├── sol3/ │ │ ├── CMakeLists.txt │ │ ├── CONTRIBUTORS.md │ │ ├── LICENSE.txt │ │ ├── README.md │ │ └── include/ │ │ └── sol/ │ │ ├── config.hpp │ │ ├── forward.hpp │ │ └── sol.hpp │ └── stb/ │ ├── CMakeLists.txt │ ├── stb_rect_pack.h │ └── stb_truetype.h ├── doc/ │ ├── ChangeLog.md │ ├── ItemDesc.md │ ├── KeyMapping.md │ ├── LICENSE.lua54 │ ├── Plugin.md │ ├── Skill.md │ └── TODO.md └── src/ ├── CMakeLists.txt ├── cfg.cpp ├── cfg.h ├── d2r/ │ ├── d2rdefs.h │ ├── mapstructs.h │ ├── processdata.cpp │ ├── processdata.h │ ├── processmanager.cpp │ ├── processmanager.h │ ├── storage.cpp │ ├── storage.h │ └── stringdefs.inl ├── data/ │ ├── d2txt.cpp │ ├── d2txt.h │ ├── gamedata.cpp │ ├── gamedata.h │ └── viewstream.h ├── main.cpp ├── plugin/ │ ├── plugin.cpp │ └── plugin.h ├── render/ │ ├── HandmadeMath.h │ ├── d2font.cpp │ ├── d2font.h │ ├── font.cpp │ ├── font.h │ ├── fontrenderimpl.h │ ├── renderer.cpp │ ├── renderer.h │ ├── ttf.cpp │ ├── ttf.h │ ├── ttfgl.cpp │ └── ttfgl.h ├── res/ │ ├── D2RMH.manifest │ ├── manifest.rc │ └── res.rc ├── ui/ │ ├── maprenderer.cpp │ ├── maprenderer.h │ ├── window.cpp │ └── window.h └── util/ ├── ntprocess.cpp ├── ntprocess.h ├── os_structs.cpp ├── os_structs.h ├── util.cpp └── util.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/build.yml ================================================ name: CMake on: push: branches: [ master ] paths: [ ".github/workflows/build.yml", CMakeLists.txt, 'src/**', 'd2mapapi/**', 'tools/**', 'inih/**' ] pull_request: branches: [ master ] paths: [ ".github/workflows/build.yml", CMakeLists.txt, 'src/**', 'd2mapapi/**', 'tools/**', 'inih/**' ] env: BUILD_TYPE: Release jobs: build: runs-on: windows-latest strategy: matrix: compiler: ["msys2_mingw", "msvc2022"] steps: - name: Install MSYS2/MinGW if: matrix.compiler == 'msys2_mingw' uses: msys2/setup-msys2@v2 with: location: D:\ update: true install: >- mingw-w64-x86_64-binutils mingw-w64-i686-binutils mingw-w64-x86_64-gcc mingw-w64-i686-gcc mingw-w64-x86_64-make mingw-w64-i686-make - name: Checkout Repository uses: actions/checkout@v2 with: fetch-depth: 0 - name: CMake Configure and Build shell: cmd working-directory: ${{github.workspace}} run: build_${{matrix.compiler}}.bat D:\msys64 - uses: actions/upload-artifact@v2 with: name: D2RMH-snapshot-${{matrix.compiler}} path: | ${{github.workspace}}/build/${{matrix.compiler}}/dist/D2RMH-snapshot.zip retention-days: 7 ================================================ FILE: .gitignore ================================================ # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib *.ilk *.pdb # Executables *.exe *.out *.app # additional exclusions *.tlog *.log .vs Debug Release *.user # Jetbrains generated files /.idea/ /cmake-*/ # Build directory(ies) **/build/ ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13) project(D2RMH C CXX) if(MSVC) set(CMAKE_CXX_STANDARD 20) else() set(CMAKE_CXX_STANDARD 17) endif() list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") include(CustomCompilerOptions) fix_compile_flags() fix_release_flags() add_static_runtime_option() add_subdirectory(deps) add_subdirectory(d2mapapi) add_subdirectory(src) ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021-2022 Soar Qin, and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ **README in other languages: [中文说明](contrib/zhCN/README.md)** # D2RMH Diablo II Resurrected map revealing tool. # Disclaimer **D2RMH is only reading process memory from D2R, without injects, hooks or memory writes, but I do not guarentee that it is totally ban-free, use at your own risk.** # What's New Check [ChangeLog](doc/ChangeLog.md) # Prerequisite * Diablo II 1.11, 1.11b, 1.12, 1.13c or 1.13d is required. You can get a minimal subset of v1.13c files [HERE](https://archive.org/details/diablo-ii-1.13c-minimal.-7z) # Usage 0. Virus/Malware detection WARNING: * If you are using Windows Defender, disable it or add D2RMH to whitelist to avoid misreporting of malware. * D2RMH can pass most Anti-Virus software detections, but not all of them, you can compile it your self if worry about it, check `How to build` section below 1. Download from `Releases` section, or any snapshot packs from `Actions` section(You need to log-in to GitHub). 2. Edit D2RMH.ini, set `d2_path` to path of your Diablo II folder, or just put extracted `D2RMH.exe` and all `.ini` files to D2 v1.13c folder. 3. Run D2RMH.exe, enjoy! # Screenshots ![Screenshot 1](screenshots/screenshot_0.png) ![Screenshot 2](screenshots/screenshot_1.png) ![Screenshot 3](screenshots/screenshot_2.png) # Plugin system * Plugins are `.lua` scripts loaded from `plugins` folder * Read [document](doc/Plugin.md) if you want to write your own plugin # TODO Check [TODO](doc/TODO.md) # How to build ## Quick instruction * Install [cmake](https://www.cmake.org/) and add `cmake\bin` to your `PATH` environment variable so that you can type `cmake` in command line to call it directly * Run `build_msvc2019.bat`, `build_msvc2022.bat`, `build_msys2_clang.bat` or `build_msys2_mingw.bat` to build. Note: You should have certain compilers intalled. For msys2 builds, install required packages as instructions below. ## Detailed instruction without .bat scripts ### MinGW GCC * Install MSYS2(https://www.msys2.org), type `pacman -Syu --noconfirm && pacman -S --noconfirm --needed make git mingw-w64-i686-toolchain mingw-w64-i686-cmake mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-cmake` in MSYS2 command line to install required components * Build D2RMH(64bit): * Open new Shell using ucrt64.exe * Clone D2RMH source by type `git clone https://github.com/soarqin/D2RMH` * Type `cd D2RMH && cmake -Bbuild -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_STATIC_CRT=ON` * Then `make -Cbuild D2RMH` to get the compiled binary in `build/bin` folder * Build d2mapapi-piped(32bit): * Open new Shell using mingw32.exe * Change current directory to D2RMH source * Type `cmake -Bbuild_d2mapapi -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_STATIC_CRT=ON d2mapapi` * Then `make -Cbuild_d2mapapi d2mapapi-piped` to get the compiled binary in `build_d2mapapi/bin` folder ### MSYS2 Clang * Mostly same as MinGW GCC, with following changes: * `mingw-w64-i686-toolchain`->`mingw-w64-clang-i686-toolchain` * `mingw-w64-i686-cmake`->`mingw-w64-clang-i686-cmake` * `mingw-w64-ucrt-x86_64-toolchain`->`mingw-w64-clang-x86_64-toolchain` * `mingw-w64-ucrt-x86_64-cmake`->`mingw-w64-clang-x86_64-cmake` * `ucrt64.exe`->`clang64.exe` * `mingw32.exe`->`clang32.exe` ### Microsoft Visual Studio 2019/2022 * Install Visual Studio 2019 or 2022 Community Edition(or Pro/Ent if you have) * Unpack downloaded source code file, or you can use git to Clone D2RMH source by type: `git clone https://github.com/soarqin/D2RMH`. Note: Using git requires [Git for windows](https://git-scm.com/download/win) installed * Build D2RMH(64bit): * (Visual Studio 2019) Type `cmake -Bbuild -G "Visual Studio 16 2019" -A x64 -DUSE_STATIC_CRT=ON` (Visual Studio 2022) Type `cmake -Bbuild -G "Visual Studio 17 2022" -A x64 -DUSE_STATIC_CRT=ON` * Now you can either: * Type `cmake --build build --config Release --target D2RMH` * Open generated `build\D2RMH.sln` and build `D2RMH` target * Compiled binaries are located in `build\bin` folder * Build d2mapapi-piped(32bit): * (Visual Studio 2019) Type `cmake -Bbuild_d2mapapi -G "Visual Studio 16 2019" -A Win32 -DUSE_STATIC_CRT=ON d2mapapi` (Visual Studio 2022) Type `cmake -Bbuild_d2mapapi -G "Visual Studio 17 2022" -A Win32 -DUSE_STATIC_CRT=ON d2mapapi` * Now you can either: * Type `cmake --build build_d2mapapi --config Release --target d2mapapi_piped` * Open generated `build_d2mapapi\d2mapapi.sln` and build `d2mapapi_piped` target * Compiled binaries are located in `build_d2mapapi\bin` folder # Credits * [d2mapapi_mod](https://github.com/soarqin/d2mapapi_mod) modified from [d2mapapi](https://github.com/jcageman/d2mapapi). * Idea and memory offsets from [MapAssist](https://github.com/misterokaygo/MapAssist). * [Handmade Math](https://github.com/HandmadeMath/Handmade-Math) for matrix calculations. * [glad](https://glad.dav1d.de) for loading OpenGL(Core)/WGL functions. * [inih](https://github.com/benhoyt/inih) for reading INI files. * [JSON for Modern C++](https://github.com/nlohmann/json) for processing JSON files. * [CascLib](https://github.com/ladislav-zezula/CascLib) for reading Casc Storage from Diablo II Resurrected. * [stb](https://github.com/nothings/stb), stb_truetype and stb_rect_pack are used. ================================================ FILE: bin/D2RMH.ini ================================================ [main] ; path to your Diablo II LOD. ; if not found in this path, D2RMH will fallback to read installation path from registry. d2_path = . ; font settings, you can: ; 1. use TTF inside D2R Casc Storage, supported fonts: ; All languages (especially koKR, ruRU, zhCN): ; blizzardglobal-v5_81.ttf(default) ; zhTW: ; blizzardglobaltcunicode.ttf(default), arfangxinshuh7c95b5_eb_t.ttf ; jaJP: ; bljap_v8_3.ttf(default), ik4ll3.ttf, tbgdb_0pp.ttf ; enUS, deDE, plPL and other latin languages: ; formal436bt.ttf(default), exocetblizzardot-medium.otf, irisl.ttf, kodia.ttf, philosopher-bolditalic.ttf ; leave empty to use default font ; 2. use external TTF(with .ttf or .ttc extension), or TBL/DC6 from D2/D2R ; a) for TTF font, just put full path in `font_file_path` ; b) for TBL/DC6 font, you should put 3 files: base.dc6, base.tbl, base.pal(you can get it from data\global\palette\fechar\pal.dat and rename it), ; and put the full path of any of the file in `font_file_path`, you can also append original font size separated by `|` if it does not match `msg_font_size`. ; for example: font_file_path = font24.dc6|24 font_file_path = font_size = 14 ; font size for message list msg_font_size = 24 ; can be: enUS,deDE,esES,frFR,itIT,koKR,plPL,esMX,jaJP,ptBR,ruRU,zhTW,zhCN ; leave empty to use D2R language settings language = [ui] ; Positive value will limit FPS to certain value, negative value will change OpenGL context swap interval: ; 0=No limit and Disable VSync ; -1=Enable VSync(aka equal to video adapter fps), ; -2=VSync with 2 frames per swap(aka video adapter fps/2), etc. ; Note: D2's logic is running at 25 frames per second, so this value is set to 25 by default fps = 25 ; 0-show map layer when in-game map is off ; 1-show map layer when in-game map is on ; 2-show map layer always show = 0 ; set this to 0 if you don't wanna map drawn on bottom game bar draw_on_game_bar = 1 ; hide map layer when certain panels are opened, 0x1FF=all panels ; 0x01-inventory panel ; 0x02-character panel ; 0x04-skill-tree panel ; 0x08-system menu ; 0x10-quest panel ; 0x20-party panel ; 0x40-mercenary panel ; 0x80-waypoint panel ; 0x100-skill selection popup panel_mask = 0x1FF ; style for guide line ; 0-draw a short line with dot ahead to target (d2hackmap style) ; 1-draw a full line to target ; 2-draw walk-path to target in lines(more CPU usage) line_style = 0 ; 0-top left ; 1-top right ; 2-center ; Note: check `map_area` entry for more info when you set position to 0 or 1 position = 2 ; restricted map_area ratio relative to D2R window(width,height) ; each value should be between 0.0 and 1.0, while 0.0 means ; adaptive size, aka suite current map size. ; you can set only one value so that width/height will both use it. ; the map will be drawn in the center of this area if map_centered=0 map_area = 0.0 ; map scaling, can be 1.0-4.0 ; cut when map_area is not set to adaptive size, or map size is larger than ; D2R game window scale = 2.0 ; 0-static map ; 1-keep player in map center map_centered = 1 ; Alpha channel(aka opacity) for whole layer (0-255) ; 0 is completely transparent, 255 is not tranparent at all ; this is stacked with other color settings, for exampe: ; while color = 128,128,128,128(alpha channel is 128), alpha = 160, ; the drawn color alpha channel is: 128*160/255=80 alpha = 255 ; bounds(in piexls) for neighbour maps ; can be set to negative to indicate recursive depth, ; e.g. -1 for show only nearby level maps ; -2 for show maps around nearby maps also ; set to 0 to disable neighbour maps show ; note: this costs CPU/VRAM a bit if is set to a large value, or nearby maps ; are large while set to negative(=2048 costs ~64MB VRAM at max), ; disable it if you get performance impact neighbour_map_bounds = -1 ; walkable area color (R,G,B) 0-255 for each channel, 0,0,0,0 = transparent walkable_color = 20,20,20,50 ; map edge color, set 0,0,0,0 to hide edge edge_color = 128,128,128,240 ; text color text_color = 255,255,255,180 ; colors for player pointer (inner/outer) player_inner_color = 255,128,128,80 player_outer_color = 51,255,255,180 non_party_player_inner_color = 255,128,128,80 non_party_player_outer_color = 255,20,20,180 ; color for guide lines line_color = 204,204,204,144 ; colors for waypoints/portals/chests/quests/wells waypoint_color = 153,153,255,200 portal_color = 255,255,102,160 chest_color = 255,102,102,160 quest_color = 102,102,255,160 shrine_color = 255,51,178,160 well_color = 51,51,255,160 ; colors for monsters/unique monsters(including champions)/npcs monster_color = 255,0,0,128 unique_monster_color = 192,166,130,204 npc_color = 160,160,160,160 ; color for doors door_color = 80,255,80,180 ; message list background color msg_bg_color = 1,1,1,128 ; message position (x,y,align) ; range of x and is [0.0,1.0], align can be 0(left), 1(centered), 2(right) msg_position = 0.95,0.25,2 ; show a text panel (updated every second), leave empty to disable ; usable patterns: ; {newline}/{n} start a newline here ; {duration} current game duration, shown as hh:mm:ss ; {time} current system time in 24h clock format: hh:mm:ss ; {timea} current system time in locale's 12h clock format ; {difficulty} current difficulty name ; {act} current act name ('ACT I' to 'ACT V') ; {mapname} current map name ; {gamename} current game name ; {gamepass} current game password ; {region} region code of the server which current game creates on ; {season} current battle.net season string text_panel_pattern = ;text_panel_pattern = {duration} ;text_panel_pattern = {difficulty}{n}{act}-{mapname}{n}{time}{n}{duration} ;text_panel_pattern = {gamename}{n}{gamepass}{n}{region} {season}{n}{difficulty}{n}{act}-{mapname}{n}{time}{n}{duration} ; text panel position, same as `msg_position` text_panel_position = 0.93,0.015,2 ; show player names show_player_names = 1 ; show npc's name show_npc_names = 1 ; show in-memory nearby objects(currently only shrines) show_objects = 1 ; minimal size of objects in pixels (except doors) object_size_minimal = 6 ; show in-memory nearby items on ground, check D2RMH_item.ini for filter settings show_items = 1 ; show in-memory nearby monsters ; 0 - not shown ; 1 - only NPCs/super-uniques/bosses/champions are shown ; 2 - common monsters and minions are also shown show_monsters = 2 ; show monster's name, monsters must be shown in `show_monsters` ; 0 - not shown ; 1 - only NPCs/super-uniques/bosses/champions are shown ; 2 - common monsters and minions are also shown show_monster_names = 1 ; show monster's enchant including aura, check [enchants] section below, monsters must be shown in `show_monsters` ; 0 - not shown ; 1 - only NPCs/super-uniques/bosses/champions are shown ; 2 - common monsters and minions are also shown show_monster_enchants = 1 ; show monster's resist(immunity), check [enchants] section below, monsters must be shown in `show_monsters` ; 0 - not shown ; 1 - only NPCs/super-uniques/bosses/champions are shown ; 2 - common monsters and minions are also shown show_monster_immunities = 2 [enchants] ; strings can be tagged with color, in form '{n}', while n is taken from d2hackmap: ; 0-text color 1-red 2-green 3-blue 4-gold 5-gray 6-black 7-gold2 ; 8-orange 9-yellow 10-green2 11-purple 12-green3 13~15-white ; if no color is set, white is selected by default. extra_strong = S extra_fast = F cursed = {2}C magic_resistant = M fire_enchanted = {1}FE ligntning_enchanted = {9}LE cold_enchanted = {3}CE mana_burn = {3}MB teleportation = T spectral_hit = H stone_skin = {4}SS multiple_shots = {12}MS fanatic = {11}F berserker = {4}B ; auras might_aura = {4}A holyFire_aura = {1}A blessedAim_aura = A holyFreeze_aura = {3}A holyShock_aura = {9}A conviction_aura = {11}A fanaticism_aura = {5}A ; immnuties physical_immunity = {4}i magic_immunity = {8}i fire_immunity = {1}i lightning_immunity = {9}i cold_immunity = {3}i poison_immunity = {2}i [sound] ; sound files used by various functions ; support WAVE files, filename with without '.wav' ext will be treated as system event sounds. ; commonly used system event sounds: ; SystemAsterisk, SystemDefault, SystemExclamation, SystemHand ; you can also check registry 'HKEY_CURRENT_USER\AppEvents\Schemes\Apps\.Default' for more ; event sounds. sound[1] = SystemDefault sound[2] = SystemAsterisk ; sound[3] = Beep.wav ================================================ FILE: bin/D2RMH_gamedata.ini ================================================ [guides] ; "string" = Map Name ; "+number" = Object with certain ID ; "-number" = Npc(Monster) with certain ID ;Act1: guide[Blood Moor][Den of Evil] =1 guide[Cold Plains][Burial Grounds] =1 guide[Stony Field][+61] =1 guide[Underground Passage Level 1][Dark Wood] =1 guide[Dark Wood][+30] =1 guide[Black Marsh][Forgotten Tower] =1 guide[Tower Cellar Level 1][Tower Cellar Level 2] =1 guide[Tower Cellar Level 2][Tower Cellar Level 3] =1 guide[Tower Cellar Level 3][Tower Cellar Level 4] =1 guide[Tower Cellar Level 4][Tower Cellar Level 5] =1 guide[Tamoe Highland][Pit Level 1] =1 guide[Pit Level 1][Pit Level 2] =1 guide[Barracks][+108] =1 guide[Jail Level 1][Jail Level 2] =1 guide[Jail Level 2][Jail Level 3] =1 guide[Jail Level 3][Inner Cloister] =1 guide[Cathedral][Catacombs Level 1] =1 guide[Catacombs Level 1][Catacombs Level 2] =1 guide[Catacombs Level 2][Catacombs Level 3] =1 guide[Catacombs Level 3][Catacombs Level 4] =1 ;Act2: guide[47][48] =1 guide[48][49] =1 guide[Dry Hills][Halls of the Dead Level 1] =1 guide[Halls of the Dead Level 1][Halls of the Dead Level 2] =1 guide[Halls of the Dead Level 2][Halls of the Dead Level 3] =1 guide[Far Oasis][Maggot Lair Level 1] =1 guide[Maggot Lair Level 1][Maggot Lair Level 2] =1 guide[Maggot Lair Level 2][Maggot Lair Level 3] =1 guide[Valley of Snakes][Claw Viper Temple Level 1] =1 guide[Claw Viper Temple Level 1][Claw Viper Temple Level 2] =1 guide[Lost City][Ancient Tunnels] =1 guide[Harem Level 1][Harem Level 2] =1 guide[Harem Level 2][Palace Cellar Level 1] =1 guide[Palace Cellar Level 1][Palace Cellar Level 2] =1 guide[Palace Cellar Level 2][Palace Cellar Level 3] =1 guide[Arcane Sanctuary][+357] =1 guide[66][+152] =1 guide[67][+152] =1 guide[68][+152] =1 guide[69][+152] =1 guide[70][+152] =1 guide[71][+152] =1 guide[72][+152] =1 ;Act3: guide[Spider Forest][Spider Cavern] =1 guide[Flayer Jungle][+252] =1 ;guide[Flayer Jungle][Flayer Dungeon Level 1] =1 Flayer Dungeon is nearby Gidbinn, no need to tip it guide[Flayer Dungeon Level 1][Flayer Dungeon Level 2] =1 guide[Flayer Dungeon Level 2][Flayer Dungeon Level 3] =1 guide[Kurast Bazaar][Ruined Temple] =1 guide[92][93] =1 guide[Travincal][Durance of Hate Level 1] =1 guide[Durance of Hate Level 1][Durance of Hate Level 2] =1 guide[Durance of Hate Level 2][Durance of Hate Level 3] =1 ;Act4: guide[Outer Steppes][Plains of Despair] =1 guide[Plains of Despair][-256] =1 guide[City of the Damned][River of Flame] =1 guide[River of Flame][+376] =1 ;Act5: guide[Arreat Plateau][Crystalline Passage] =1 guide[Crystalline Passage][Frozen River] =1 guide[Frozen River][+460] =1 guide[Glacial Trail][Frozen Tundra] =1 guide[Nihlathak's Temple][Halls of Anguish] =1 guide[Halls of Anguish][Halls of Pain] =1 guide[Halls of Pain][Halls of Vaught] =1 guide[Halls of Vaught][+462] =1 guide[Frozen Tundra][The Ancients' Way] =1 guide[The Ancients' Way][Arreat Summit] =1 guide[Worldstone Keep Level 1][Worldstone Keep Level 2] =1 guide[Worldstone Keep Level 2][Worldstone Keep Level 3] =1 guide[Worldstone Keep Level 3][Throne of Destruction] =1 [useful_object_op] 2=Shrine 10=Quest ;Gibbet of Cairn 12=Quest ;Tree of Inifuss 15=Portal ;Player's Portal, or Permanent Portal by shrines 21=Quest ;Malus 22=Well,well 23=Waypoint 24=Quest ;Tainted Sun Altar 25=Quest ;Where you place the Horadric staff 28=Quest ;Lam Esen's Tome 31=Quest ;Gidbinn 34=Portal ;Arcane Sanctuary portal 39=Chest,box ;Horadric Cube Chest 40=Chest,tr1 ;Horadric Scroll Chest 41=Chest,Staff of Kings ;Staff Of Kings Chest 42=Quest ;Horazon's Journal 43=Portal ;Portal to Duriel's Lair 46=Portal ;Hellgate 49=Quest ;Hellforge 57=Chest,qhr ;Khalim's Heart 58=Chest,qey ;Khalim's Eye 59=Chest,qbr ;Khalim's Brain 69=Chest ;Evil Urn [useful_objects] 61=Quest,StoneAlpha ;Cairn Stones 397=Chest ;Sparky Chest 460=Quest,Drehya ;Anya 462=Quest,Nihlathak ;Nihlathak 473=Quest ;Caged Barbarian 580=Chest,chest ;Unique Chest [useful_npcs] 256=Quest ;Izual ================================================ FILE: bin/D2RMH_item.ini ================================================ [items] ; format: ; [+quality][*sockets][#ethereal]=style[,color,[sound]] ; ID or Code: check `ID` and `code` in doc/ItemDesc.md ; quality: 0-low 1-normal 2-superior 3-magic 4-set 5-rare 6-unique 7-crafted ; ethereal: 0 or 1 ; all other fields can be single value or range/list ; you can use `642` or `r33` for Zod Rune ; use `640,642` or `r31,r33` for both Jah and Zod Rune ; use `640-642` or `r31-r33` for Jah, Cham and Zod Rune ; mix them `634,636-638,640-642` or `r24,r26-r29,r31-r33` ; style: 0-don't show 1-show on minimap 2-show on a separate message list 3-show both ; color: 0-default 1-red 2-green 3-blue 4-gold 5-gray 6-black 7-gold2 ; 8-orange 9-yellow 10-green2 11-purple 12-green3 13~15-white ; use quality color by default if set to 0 or absent: ; low/normal=15, superior=5, magic=3, set=2, rare=9, unique=4, crafted=8 ; sound: index for sound playing on drop, check `[sound]` section in D2RMH.ini. 0 for mute, as default. ; full example: `crs+2,3*0,4,6=1,0,1` shows "Normal/Superior 0/4/6 Sockets Crystal Sword" on minimap ; with default color and play sound[1] in `[sound]` section in D2RMH.ini. ; Items for Charsi's imbue ; Dimensional Shard 300+1,2*0#0 = 0 ; Diadem 421+1,2*0#0 = 0 ; Items for crafting ; Vampirefang Belt 461+3 = 0 ; Gloves 334-338,380-384,450-454+3 = 0 ; Monarch Shield 447+3#0 = 0 ; Circlet, Coronet, Tiara, Diadem 418-421+3#0 = 0 ; Amulet, Ring 520,522+3 = 0 ; Elite Light Armors 429-432,436,443+3#0 = 0 ; Assa Claws 182-188,189-195+3 = 0 ; Sorc Orbs 276-280,286-290,296-300+3 = 0 ; Ancient Armor 326+3#0 = 0 ; Embossed Plate 370+3#0 = 0 ; Ornate Plate 372+3#0 = 0 ; Elite Medium Armors 433,435,437,439,440,442+3#0 = 0 ; Battle Boots, Mirrored Boots 388,458+3 = 0 ; Druid Helmets ; Alpha Helm, Griffon Headdress, Hunter's Guise, Sacred Feathers, Totemic Mask, Blood Spirit, Sun Spirit, Earth Spirit, Sky Spirit, Dream Spirit 468-472,488-492+3#0 = 0 ; Bar Helmets 403-407,473-477,493-497+3#0 = 0 ; Potential good rare items ; Short Sword, Falchion ; Gladius, Tulwar ; Falcata, Hydra Edge 25,28,118,121,221,224+5#1 = 0 ; Crystal Sword, Dimensional Blade 29,122+5#1 = 0 ; Broad Sword, War Sword ; Battle Sword, Ancient Sword ; Conquest Sword, Mythical Sword 30,32,123,125,226,228+5#1 = 0 ; Axe, Double Axe, Military Pick, War Axe ; Cleaver, Twin Axe, Crowbill, Naga ; Small Crescent, Ettin Axe, War Spike, Berserker Axe 1-4,94-97,197-200+5#1 = 0 ; Morning Star, War Hammer ; Jagged Star, Battle Hammer ; Devil Star, Legendary Mallet 20,22,113,115,216,218+5#1 = 0 ; Grand Scepter, War Scepter ; Holy Water Sprinkler, Divine Scepter ; Seraph Rod, Caduceus 16-17,109-110,212-213+5#1 = 0 ; Large Axe, Broad Axe, Battle Axe, Great Axe, Giant Axe ; Military Axe, Bearded Axe, Tabar, Gothic Axe, Ancient Axe ; Feral Axe, Silver-edged Axe, Decapitator, Champion Axe, Glorious Axe 5-9,98-102,201-205+5#1 = 0 ; Maul, Great Maul ; War Club, Martel de Fer ; Ogre Maul, Thunder Maul 23-24,116-117,219-220+5#1 = 0 ; Two-Handed Sword, Claymore, Giant Sword, Bastard Sword, Flamberge, Great Sword ; Espandon, Dacian Falx, Tusk Sword, Gothic Sword, Zweihander, Executioner Sword ; Legend Sword, Highland Blade, Balrog Blade, Champion Sword, Colossus Sword, Colossus Blade 33-38,126-131,229-234+5#1 = 0 ; Spear, Trident ; War Spear, Fuscina ; Hyperion Spear, Stygian Pike 52-53,145-146,248-249+5#1 = 0 ; Spetum, Pike, Bardiche, Voulge, Scythe, Poleaxe, Halberd, War Scythe ; Yari, Lance, Lochaber Axe, Bill, Battle Scythe, Partizan, Bec-de-Corbin, Grim Scythe ; Ghost Spear, War Pike, Ogre Axe, Colossus Voulge, Thresher, Cryptic Axe, Great Poleaxe, Giant Thresher 55-62,148-155,251-258+5#1 = 0 ; Maiden Spear, Maiden Pike ; Ceremonial Spear, Ceremonial Pike ; Matriarchal Spear, Matriarchal Pike 283-284,293-294,303-304+5#1 = 0 ; Maiden Javelin, Ceremonial Javelin, Matriarchal Javelin 285,295,305+5 = 0 ; Short Staff, Long Staff, Gnarled Staff, Battle Staff, War Staff ; Jo Staff, Quarterstaff, Cedar Staff, Gothic Staff, Rune Staff ; Walking Stick, Stalagmite, Elder Staff, Shillelagh, Archon Staff 63-67,156-160,259-263+5#1 = 0 ; Throwing Knife, Throwing Axe, Balanced Knife, Balanced Axe, Javelin, Pilum, Short Spear, Glaive, Throwing Spear ; Battle Dart, Francisca, War Dart, Hurlbat, War Javelin, Great Pilum, Simbilan, Spiculum, Harpoon ; Flying Knife, Flying Axe, Winged Knife, Winged Axe, Hyperion Javelin, Stygian Pilum, Balrog Spear, Ghost Glaive, Winged Harpoon 43-51,136-144,239-247+5#1 = 0 ; Maiden Javelin, Ceremonial Javelin, Matriarchal Javelin 285,295,305+5#1 = 0 ; Short Bow, Hunter's Bow, Long Bow, Composite Bow, Short Battle Bow, Long Battle Bow, Short War Bow, Long War Bow ; Edge Bow, Razor Bow, Cedar Bow, Double Bow, Short Siege Bow, Large Siege Bow, Rune Bow, Gothic Bow ; Spider Bow, Blade Bow, Shadow Bow, Great Bow, Diamond Bow, Crusader Bow, Ward Bow, Hydra Bow 68-75,161-168,264-271+5 = 0 ; Stag Bow, Reflex Bow ; Ashwood Bow, Ceremonial Bow ; Matriarchal Bow, Grand Matron Bow 281-282,291-292,301-302+5 = 0 ; Light Crossbow, Crossbow, Heavy Crossbow, Repeating Crossbow ; Arbalest, Siege Crossbow, Ballista, Chu-Ko-Nu ; Pellet Bow, Gorgon Crossbow, Colossus Crossbow, Demon Crossbow 76-79,169-172,272-275+5 = 0 ; Boots 340-343,385-389,455-459+5 = 0 ; Gloves 334-338,380-384,450-454+5 = 0 ; Claws 175-195+5 = 0 ; Circlet, Coronet, Tiara, Diadem 418-421+5 = 0 ; Sorc Orbs 276-280,286-290,296-300+5 = 0 ; Necro Shields 413-417,483-487,503-507+5 = 0 ; Amulet, Ring 520,522+5 = 0 ; Necro Wands 103-106,206-209+5 = 0 ; Set Items ; Winged Helm = Guillaume's Face 356+4 = 3 ; Diadem = M'avina's True Sight 421+4 = 3 ; Lacquered Plate = Tal Rasha's Guardianship 440+4 = 3 ; Mesh Belt = Tal Rasha's Fine-Spun Cloth 392+4 = 3 ; Amulet = Tal Rasha's Adjudication or other set amulets 520+4 = 3 ; Shadow Plate = Aldur's Deception 441+4 = 3 ; Sacred Armor = Immortal King's Soul Cage 442+4 = 3 ; Troll Belt = Trang-Oul's Girth 463+4 = 3 ; Bone Visage = Trang-Oul's Guise 465+4 = 3 ; Chaos Armor = Trang-Oul's Scales 371+4 = 3 ; Heavy Bracers = Trang-Oul's Claws 382+4 = 3 ; Cantor Trophy = Trang-Oul's Wing 486+4 = 3 ; Corona = Griswold's Valor 427+4 = 3 ; Vortex Shield = Griswold's Honor 502+4 = 3 ; Caduceus = Griswold's Redemption 213+4 = 3 ; Scissors Suwayyah = Natalya's Mark 195+4 = 3 ; Colossus Blade = Bul-Kathos' Sacred Charge 234+4 = 3 ; Mythical Sword = Bul-Kathos' Tribal Guardian 228+4 = 3 ; Unique Items ; Chain Gloves = Chance Guards 336+6 = 3 ; Light Gauntlets = Magefist 337+6 = 3 ; Vampirebone Gloves = Dracul's Grasp 451+6 = 3 ; Ogre Gauntlets = Steelrend 454+6 = 3 ; Serpentskin Armor = Skin of the Vipermagi 360+6 = 3 ; Mesh Armor = Shaftstop 365+6 = 3 ; Cuirass = Duriel's Shell 366+6#1 = 3 ; Russet Armor = Skullder's Ire 367+6 = 3 ; Templar Coat = Guardian Angel 368+6 = 3 ; Dusk Shroud = Ormus' Robes 429+6 = 3 ; Wire Fleece = The Gladiator's Bane 432+6 = 3 ; Balrog Skin = Arkain's Valor 437+6 = 3 ; Kraken Shell = Leviathan 439+6 = 3 ; Sacred Armor = Templar's Might, Tyrael's Might 442+6 = 3 ; Sharkskin Boots = Waterwalk 386+6 = 3 ; Mesh Boots = Silkweave 387+6 = 3 ; Battle Boots = Wartraveler 388+6 = 3 ; War Boots = Gorerider 389+6 = 3 ; Scarabshell Boots = Sandstorm Trek 456+6 = 3 ; Boneweave Boots = Marrowwalk 457+6 = 3 ; Myrmidon Greaves = Shadowdancer 459+6 = 3 ; Sharkskin Belt = Razortail 391+6 = 3 ; War Belt = Thundergold's Vigor 394+6 = 3 ; Spiderweb Sash = Archnid Mesh 460+6 = 3 ; Vampirefang Belt = Nosferatu's Coil 461+6 = 3 ; Mithril Coil = Verdugo's Hearty Cord 462+6 = 3 ; Monarch = Stormshield 447+6 = 3 ; Gilded Shield = Herald of Zakarum 481+6 = 3 ; Hierophant Trophy = Homunculus 487+6 = 3 ; Succubus Skull = Boneflame 506+6 = 3 ; Bloodlord Skull = Darkforce Spawn 507+6 = 3 ; Sallet = Rockstopper 353+6#1 = 3 ; Casque = Stealskull 354+6 = 3 ; Grand Crown = Crown of Thieves 357+6#1 = 3 ; Grim Helm = Vampiregaze 395+6#1 = 3 ; Tiara = Kira's Guardian 420+6 = 3 ; Shako = Harlequin Crest 422+6 = 3 ; Spired Helm = Veil of Steel 426+6 = 3 ; Demonhead = Andariel's Visage 428+6 = 3 ; Totemic Mask = Jalal's Mane 472+6 = 3 ; Slayer Guard = Arreat's Face 477+6 = 3 ; Corona = Crown of Ages 427+6 = 3 ; Diadem = Griffon's Eye 421+6 = 3 ; Tomb Wand = Arm of King Leoric 105+6 = 3 ; Battle Sword = Headstriker 123+6#1 = 3 ; Fuscina = Kelpie Snare 146+6#1 = 3 ; Yari = Hone Sundan 148+6#1 = 3 ; Ballista = Buriza-Do Kyanon 171+6 = 3 ; Greater Talons = Bartuc's Cut-Throat 187+6 = 3 ; Lich Wand = Boneshade 208+6 = 3 ; Mighty Scepter = Heaven's Light 211+6 = 3 ; Scourge = Horizon's Tornado 217+6 = 3 ; Phase Blade = Lightsabre, Azurewrath 225+6 = 3 ; Champion Sword = Doombringer 232+6 = 3 ; Winged Knife = Warshrike 241+6#1 = 3 ; Winged Axe = Lacerator 242+6#1 = 3 ; Hyperion Spear = Arioc's Needle 248+6#1 = 3 ; Ogre Axe = Bonehew 253+6#1 = 3 ; Thresher = The Reaper's Toll 255+6 = 3 ; Cryptic Axe = Tomb Reaver 256+6 = 3 ; Elder Staff = Ondal's Wisdom 261+6 = 3 ; Crusader Bow = Eaglehorn 269+6 = 3 ; Ward Bow = Widowmaker 270+6 = 3 ; Swirling Crystal = The Oculus 290+6 = 3 ; Eldritch Orb = Eschuta's Temper 297+6 = 3 ; Ceremonial Javelin = Titan's Revenge 295+6 = 3 ; Unearthed Wand = Death's Web 209+6 = 3 ; Caduceus = Astreon's Iron Ward 213+6 = 3 ; Archon Staff = Mang Song's Lesson 263+6 = 3 ; Dimensional Shard = Death's Fathom 300+6 = 3 ; Colossus Blade = Godfather 234+6 = 3 ; Legend Spike = Ghostflame 238+6 = 3 ; Berserker Axe = Death Cleaver 200+6 = 3 ; Glorious Axe = Executioner's Justice 205+6 = 3 ; Giant Thresher = Stormspire 258+6 = 3 ; War Pike = Steel Pillar 252+6 = 3 ; Hydra Bow = Windforce 271+6 = 3 ; Winged Harpoon = Gargoyle's Bite 247+6 = 3 ; Thunder Maul = Earth Shifter, The Cranium Basher 220+6 = 3 ; Unique Amulet, Ring 520,522+6 = 3 ; Unique Small Charm, Large Charm, Grand Charm 603-605+6 = 3 ; Jewels 643 = 3 ; Rare Jewel 643+5 = 3 ; Unique Jewel 643+6 = 3 ; Small Charm, Grand Charm 603,605 = 3 ; Large Charm 604 = 0 ; Items for Runewords ; Grand Crown, Corona 357,427+2*0#0 = 3 ; Grand Crown, Corona 357,427+2*3#0 = 3 ; Zweihander 130+1,2*0#0 = 3 ; Zweihander 130+1,2*5#0 = 3 ; Colossus Blade 234+2*0#1 = 3 ; Colossus Blade 234+2*4#0 = 3 ; Colossus Blade 234+2*4,5,6#1 = 3 ; Colossus Sword 233+2*4#1 = 3 ; Berserker Axe 200+1,2*0,4,5,6#1 = 3 ; Berserker Axe 200+2*4,5#0 = 3 ; Berserker Axe 200+1*0#0 = 3 ; Berserker Axe 200+1*5#0 = 3 ; Ghost Spear, War Pike 251,252+2*0,6#1 = 3 ; Matriarchal Spear, Matriarchal Pike 303,304+2*0,6#1 = 3 ; Scythe 59+1*0#0 = 0 59+1*0#1 = 0 59+1,2*4 = 0 ; Gnarled Staff, Battle Staff ; Cedar Staff, Gothic Staff ; Elder Staff, Shillelagh 65-66,158-159,261-262+1,2*0#0 = 0 65-66,158-159,261-262+1,2*0#1 = 0 65-66,158-159,261-262+1,2*4 = 0 ; War Staff, Rune Staff, Archon Staff 67,160,263+1*0#0 = 0 67,160,263+1*0#1 = 0 67,160,263+1,2*4 = 0 ; Thresher, Cryptic Axe 255,256+1,2*0,4,5#1 = 0 ; Great Poleaxe 257+1*0,4,5#1 = 3 257+2*0,4,5,6#1 = 3 ; Giant Thresher 258+1*0,4,5#1 = 3 258+2*4,5#1 = 3 ; Crystal Sword 29+1,2*6 = 0 29+1*0#0 = 3 29+1*0#1 = 3 29+1,2*3,5 = 3 ; Phase Blade 225+1,2*0#0 = 3 225+1,2*3,5#0 = 3 225+1,2*6#0 = 0 ; Flail 21+1*0#0 = 3 ; Flail 21+1*0#1 = 3 21+1,2*4 = 3 ; War Scepter, Divine Scepter, Caduceus 17,110,213+1,2*0#0 = 0 17,110,213+1,2*0#1 = 0 17,110,213+1,2*5 = 0 ; Great Bow 267+2*0#0 = 3 267+2*4#0 = 3 ; Shadow Bow 266+2*4#0 = 3 ; Grand Matron Bow 302+2*4#0 = 3 ; Suwayyah, Scissors Suwayyah, Runic Talons, Feral Claws 189,195,194,193+1,2*0#0 = 3 189,195,194,193+1,2*3#0 = 3 ; Sacred Targe, Sacred Rondache, Kurast Shield, Zakarum Shield, Vortex Shield 498-502+1,2*0#0 = 3 498-502+1,2*3,4#0 = 3 498-502+1,2*0,4#1 = 3 ; Monarch 447+1,2*0#0 = 3 447+1,2*4#0 = 3 ; Troll Nest 466+1,2*0#0 = 3 466+1,2*3#0 = 3 ; Mage Plate 373+2*0#0 = 3 373+2*3#0 = 3 ; Lacquered Plate 440+1*0#1 = 3 ; Sacred Armor 442+2*0#0 = 3 ; Sacred Armor 442+1*0#1 = 3 ; Sacred Armor 442+2*3,4#0 = 3 ; Shadow Plate 441+1*0#1 = 3 ; Archon Plate 443+2*0#0 = 3 ; Archon Plate 443+1*0#1 = 3 ; Archon Plate 443+2*3,4#0 = 3 ; Dusk Shroud 429+2*0,3,4#0 = 0 ; Great Hauberk 436+2*0,3,4#0 = 0 ; Bone Wand, Grim Wand, Petrified Wand, Tomb Wand, Grave Wand, Polished Wand, Ghost Wand, Lich Wand, Unearthed Wand 12,13,104-106,206-209+1,2*0#0 = 0 12,13,104-106,206-209+1,2*2#0 = 0 12,13,104-106,206-209+1,2*0,2#1 = 0 ; Preserved Head, Zombie Head, Unraveller Head, Gargoyle Head, Demon Head ; Mummified Trophy, Fetish Trophy, Sexton Trophy, Cantor Trophy, Hierophant Trophy ; Minion Skull, Hellspawn Skull, Overseer Skull, Succubus Skull, Bloodlord Skull 413-417,483-487,503-507+1,2*0#0 = 0 413-417,483-487,503-507+1,2*2#0 = 0 ; Key of Destruction 649 = 3 ; Key of Hate 648 = 3 ; Key of Terror 647 = 3 ; Mephisto's Brain 652 = 3 ; Baal's Eye 651 = 3 ; Diablo's Horn 650 = 3 ; Token of Absolution 653 = 3 ; Twisted Essence of Suffering 654 = 0 ; Charged Essence of Hatred 655 = 0 ; Burning Essence of Terror 656 = 0 ; Festering Essence of Destruction 657 = 0 ; Wirt's Leg 88 = 0 ; Act1 ; Scroll of Inifuss, Key to the Cairn Stones, Horadric Malus 524,525,89 = 0 ; Act2 ; Horadric Scroll, Book of Skill, Horadric Cube, Shaft of the Horadric Staff, Top of the Horadric Staff, Horadric Staff 550,552,549,92,521,91 = 0 ; Act3 ; The Gidbinn, Potion of Life, A Jade Figurine, The Golden Bird, Lam Esen's Tome, Khalim's Eye, Khalim's Heart, Khalim's Brain, Khalim's Flail, Khalim's Will 87,545-548,553-555,173-174 = 0 ; Act4 ; Mephisto's Soulstone, Hell Forge Hammer 551,90 = 0 ; Act5 ; Malah's Potion, Scroll of Resistance 644,646 = 0 ; Arrows 526 = 0 ; Bolts 528 = 0 ; Key 543 = 0 ; Minor Healing Potion, Light Healing Potion, Healing Potion, Greater Healing Potion, Super Healing Potion 587-591 = 0 ; Minor Mana Potion, Light Mana Potion, Mana Potion, Greater Mana Potion, Super Mana Potion 592-596 = 0 ; Tome of Town Portal, Tome of Identify 518-519 = 0 ; Scroll of Town Portal, Scroll of Identify 529-530 = 0 ; Rejuvenation Potion 515 = 0 ; Full Rejuvenation Potion 516 = 3 ; Flawed Emerald, Flawed Ruby, Flawed Diamond, Diamond 573,578,583,584 = 0 ; Amethyst, Topaz, Sapphire, Emerald, Ruby 559,564,569,574,579 = 0 ; Flawless Amethyst, Perfect Amethyst 560-561 = 3 ; Flawless Topaz, Perfect Topaz 565-566 = 3 ; Flawless Sapphire, Perfect Sapphire 570-571 = 3 ; Flawless Emerald, Perfect Emerald 575-576 = 3 ; Flawless Ruby, Perfect Ruby 580-581 = 3 ; Flawless Diamond, Perfect Diamond 585-586 = 3 ; Flawless Skull, Perfect Skull 600-601 = 3 ; Runes r01-r13 = 3 r14-r33 = 3,0,1 ================================================ FILE: bin/plugins/chicken_life.lua ================================================ local text = create_text_list("default") local chicken_life = function() local player = get_player() if player and player.map ~= 1 and player.map ~= 40 and player.map ~= 75 and player.map ~= 103 and player.map ~= 109 then if player.stats[7] < player.stats[8] * 0.3 then kill_process() end end end function chicken_life_toggle(on) if on then text:add('Chicken life enabled. Toggle hotkey: \'Ctrl+/\'', 5000, -1) else text:add('Chicken life disabled. Toggle hotkey: \'Ctrl+/\'', 5000, -1) end end register_plugin("Ctrl+/", false, 250, chicken_life, chicken_life_toggle) ================================================ FILE: bin/plugins/hotkey.lua ================================================ local toggle_show = function() local conf = get_config() conf.show = conf.show + 1 if conf.show > 2 then conf.show = 0 end end local zoom_in = function() local conf = get_config() if conf.scale < 4.0 then conf.scale = conf.scale + 0.5 if conf.scale > 4.0 then conf.scale = 4.0 end flush_overlay() end end local zoom_out = function() local conf = get_config() if conf.scale > 1.0 then conf.scale = conf.scale - 0.5 if conf.scale < 1.0 then conf.scale = 1.0 end flush_overlay() end end register_hotkey('\\', toggle_show) register_hotkey('OEM_PLUS', zoom_in) register_hotkey('OEM_MINUS', zoom_out) ================================================ FILE: bin/plugins/town_portal_check.lua ================================================ local text = create_text_list("default") local town_portal_check = function() -- game fully loaded? if not get_skill(0) then return end local quantity local portal_book = get_skill(220) if portal_book then quantity = portal_book.quantity else quantity = 0 end if quantity < 3 then text:add(string.format("\x0BWARNING! Town portal scrolls quantity low: %d", quantity), 1500, 0) end end register_plugin(1000, town_portal_check) ================================================ FILE: build_msvc2019.bat ================================================ @echo off setlocal set BUILD_DIR=msvc2019 setlocal set PATH=%MSYS2_BASE_PATH%\mingw64\bin;%PATH% cmake -Bbuild/%BUILD_DIR%/main -G "Visual Studio 16 2019" -A x64 -DUSE_STATIC_CRT=ON . cmake --build build/%BUILD_DIR%/main --config Release --target D2RMH -j endlocal setlocal set PATH=%MSYS2_BASE_PATH%\mingw32\bin;%PATH% cmake -Bbuild/%BUILD_DIR%/d2mapapi -G "Visual Studio 16 2019" -A Win32 -DUSE_STATIC_CRT=ON d2mapapi cmake --build build/%BUILD_DIR%/d2mapapi --config Release --target d2mapapi_piped -j endlocal cmake -E make_directory build/%BUILD_DIR%/dist cmake -E copy_if_different build\%BUILD_DIR%\main\bin\Release\D2RMH.exe build\%BUILD_DIR%\d2mapapi\bin\Release\d2mapapi_piped.exe build\%BUILD_DIR%\dist\ call copy_dist.bat endlocal ================================================ FILE: build_msvc2022.bat ================================================ @echo off setlocal set BUILD_DIR=msvc2022 setlocal set PATH=%MSYS2_BASE_PATH%\mingw64\bin;%PATH% cmake -Bbuild/%BUILD_DIR%/main -G "Visual Studio 17 2022" -A x64 -DUSE_STATIC_CRT=ON . cmake --build build/%BUILD_DIR%/main --config Release --target D2RMH -j endlocal setlocal set PATH=%MSYS2_BASE_PATH%\mingw32\bin;%PATH% cmake -Bbuild/%BUILD_DIR%/d2mapapi -G "Visual Studio 17 2022" -A Win32 -DUSE_STATIC_CRT=ON d2mapapi cmake --build build/%BUILD_DIR%/d2mapapi --config Release --target d2mapapi_piped -j endlocal cmake -E make_directory build/%BUILD_DIR%/dist cmake -E copy_if_different build\%BUILD_DIR%\main\bin\Release\D2RMH.exe build\%BUILD_DIR%\d2mapapi\bin\Release\d2mapapi_piped.exe build\%BUILD_DIR%\dist\ call copy_dist.bat endlocal ================================================ FILE: build_msys2_clang.bat ================================================ @echo off setlocal set BUILD_DIR=msys2_clang setlocal set MSYS2_BASE_PATH=%~1 if "%~1" == "" set MSYS2_BASE_PATH=C:\msys64 setlocal set PATH=%MSYS2_BASE_PATH%\clang64\bin;%PATH% cmake -Bbuild/%BUILD_DIR%/main -G "MinGW Makefiles" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DUSE_STATIC_CRT=ON . cmake --build build/%BUILD_DIR%/main --target D2RMH -j endlocal setlocal set PATH=%MSYS2_BASE_PATH%\clang32\bin;%PATH% cmake -Bbuild/%BUILD_DIR%/d2mapapi -G "MinGW Makefiles" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DUSE_STATIC_CRT=ON d2mapapi cmake --build build/%BUILD_DIR%/d2mapapi --target d2mapapi_piped -j endlocal endlocal cmake -E make_directory build/%BUILD_DIR%/dist cmake -E copy_if_different build\%BUILD_DIR%\main\bin\D2RMH.exe build\%BUILD_DIR%\d2mapapi\bin\d2mapapi_piped.exe build\%BUILD_DIR%\dist\ call copy_dist.bat endlocal ================================================ FILE: build_msys2_mingw.bat ================================================ @echo off setlocal set BUILD_DIR=msys2_mingw setlocal set MSYS2_BASE_PATH=%~1 if "%~1" == "" set MSYS2_BASE_PATH=C:\msys64 setlocal set PATH=%MSYS2_BASE_PATH%\mingw64\bin;%PATH% cmake -Bbuild/%BUILD_DIR%/main -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_STATIC_CRT=ON . cmake --build build/%BUILD_DIR%/main --target D2RMH -j endlocal setlocal set PATH=%MSYS2_BASE_PATH%\mingw32\bin;%PATH% cmake -Bbuild/%BUILD_DIR%/d2mapapi -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_STATIC_CRT=ON d2mapapi cmake --build build/%BUILD_DIR%/d2mapapi --target d2mapapi_piped -j endlocal endlocal cmake -E make_directory build/%BUILD_DIR%/dist cmake -E copy_if_different build\%BUILD_DIR%\main\bin\D2RMH.exe build\%BUILD_DIR%\d2mapapi\bin\d2mapapi_piped.exe build\%BUILD_DIR%\dist\ call copy_dist.bat endlocal ================================================ FILE: cmake/CustomCompilerOptions.cmake ================================================ macro(fix_compile_flags) if(MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") endif() endmacro() macro(fix_release_flags) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s -flto") set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} -s -flto") set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -s -flto") set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL} -s -flto") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -flto") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") endif() if(MSVC) add_compile_options( $<$:/GL> $<$:/GL> ) add_link_options( $<$:/LTCG> $<$:/LTCG> ) endif() endmacro() macro(add_static_runtime_option) option(USE_STATIC_CRT "Use static C runtime" OFF) if(USE_STATIC_CRT) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static -static-libgcc -static-libstdc++") elseif(MSVC) set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO ) foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") set(${CompilerFlag} "${${CompilerFlag}}" CACHE STRING "msvc compiler flags" FORCE) endforeach() add_compile_options( $<$:/MT> $<$:/MTd> $<$:/MT> $<$:/MT> $<$:/MT> ) endif() endif() endmacro() ================================================ FILE: cmake/GetVersion.cmake ================================================ macro(get_project_version VER_PROJ_NAME) find_package(Git QUIET) # Check if git is found... if (GIT_FOUND) # Get last tag from git execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --tags WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ${VER_PROJ_NAME}_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE) # Get name of current branch execute_process(COMMAND ${GIT_EXECUTABLE} symbolic-ref --short HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ${VER_PROJ_NAME}_BRANCH_NAME OUTPUT_STRIP_TRAILING_WHITESPACE) #How many commits since last tag execute_process(COMMAND ${GIT_EXECUTABLE} rev-list ${${VER_PROJ_NAME}_BRANCH_NAME} ${${VER_PROJ_NAME}_VERSION_STRING}..HEAD --count WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ${VER_PROJ_NAME}_VERSION_AHEAD OUTPUT_STRIP_TRAILING_WHITESPACE) # Get current commit SHA from git execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ${VER_PROJ_NAME}_VERSION_GIT_SHA OUTPUT_STRIP_TRAILING_WHITESPACE) # Get partial versions into a list string(REGEX MATCHALL "-.*$|[0-9]+" ${VER_PROJ_NAME}_PARTIAL_VERSION_LIST ${${VER_PROJ_NAME}_VERSION_STRING}) # Set the version numbers list(GET ${VER_PROJ_NAME}_PARTIAL_VERSION_LIST 0 ${VER_PROJ_NAME}_VERSION_MAJOR) list(GET ${VER_PROJ_NAME}_PARTIAL_VERSION_LIST 1 ${VER_PROJ_NAME}_VERSION_MINOR) list(GET ${VER_PROJ_NAME}_PARTIAL_VERSION_LIST 2 ${VER_PROJ_NAME}_VERSION_PATCH) # The tweak part is optional, so check if the list contains it list(LENGTH ${VER_PROJ_NAME}_PARTIAL_VERSION_LIST ${VER_PROJ_NAME}_PARTIAL_VERSION_LIST_LEN) if (${VER_PROJ_NAME}_PARTIAL_VERSION_LIST_LEN GREATER 3) list(GET ${VER_PROJ_NAME}_PARTIAL_VERSION_LIST 3 ${VER_PROJ_NAME}_VERSION_TWEAK) string(SUBSTRING ${${VER_PROJ_NAME}_VERSION_TWEAK} 1 -1 ${VER_PROJ_NAME}_VERSION_TWEAK) endif() # Unset the list unset(${VER_PROJ_NAME}_PARTIAL_VERSION_LIST) # Save version to file (which will be used when Git is not available # or VERSION_UPDATE_FROM_GIT is disabled) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/version_generated ${${VER_PROJ_NAME}_VERSION_STRING} "*" ${${VER_PROJ_NAME}_VERSION_MAJOR} "*" ${${VER_PROJ_NAME}_VERSION_MINOR} "*" ${${VER_PROJ_NAME}_VERSION_PATCH} "*" ${${VER_PROJ_NAME}_VERSION_TWEAK} "*" ${${VER_PROJ_NAME}_VERSION_AHEAD} "*" ${${VER_PROJ_NAME}_VERSION_GIT_SHA}) else() # Git not available, get version from file file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/version_generated ${VER_PROJ_NAME}_VERSION_LIST) string(REPLACE "*" ";" ${VER_PROJ_NAME}_VERSION_LIST "${${VER_PROJ_NAME}_VERSION_LIST}") # Set partial versions list(GET ${VER_PROJ_NAME}_VERSION_LIST 0 ${VER_PROJ_NAME}_VERSION_STRING) list(GET ${VER_PROJ_NAME}_VERSION_LIST 1 ${VER_PROJ_NAME}_VERSION_MAJOR) list(GET ${VER_PROJ_NAME}_VERSION_LIST 2 ${VER_PROJ_NAME}_VERSION_MINOR) list(GET ${VER_PROJ_NAME}_VERSION_LIST 3 ${VER_PROJ_NAME}_VERSION_PATCH) list(GET ${VER_PROJ_NAME}_VERSION_LIST 4 ${VER_PROJ_NAME}_VERSION_TWEAK) list(GET ${VER_PROJ_NAME}_VERSION_LIST 5 ${VER_PROJ_NAME}_VERSION_AHEAD) list(GET ${VER_PROJ_NAME}_VERSION_LIST 6 ${VER_PROJ_NAME}_VERSION_GIT_SHA) endif() # Set full project version string if (${VER_PROJ_NAME}_VERSION_AHEAD GREATER 0) set(${VER_PROJ_NAME}_VERSION_STRING_FULL ${${VER_PROJ_NAME}_VERSION_STRING}+${${VER_PROJ_NAME}_VERSION_AHEAD}.${${VER_PROJ_NAME}_VERSION_GIT_SHA}) else() set(${VER_PROJ_NAME}_VERSION_STRING_FULL ${${VER_PROJ_NAME}_VERSION_STRING}.${${VER_PROJ_NAME}_VERSION_GIT_SHA}) endif() # Set project version (without the preceding 'v') set(${VER_PROJ_NAME}_VERSION ${${VER_PROJ_NAME}_VERSION_MAJOR}.${${VER_PROJ_NAME}_VERSION_MINOR}.${${VER_PROJ_NAME}_VERSION_PATCH}) if (${VER_PROJ_NAME}_VERSION_TWEAK) set(${VER_PROJ_NAME}_VERSION ${${VER_PROJ_NAME}_VERSION}-${${VER_PROJ_NAME}_VERSION_TWEAK}) endif() target_compile_definitions(${VER_PROJ_NAME} PRIVATE VERSION_STRING_FULL="${${VER_PROJ_NAME}_VERSION_STRING_FULL}" VERSION_STRING="${${VER_PROJ_NAME}_VERSION_STRING}" VERSION_MAJOR=${${VER_PROJ_NAME}_VERSION_MAJOR} VERSION_MINOR=${${VER_PROJ_NAME}_VERSION_MINOR} VERSION_PATCH=${${VER_PROJ_NAME}_VERSION_PATCH} VERSION_TWEAK="${${VER_PROJ_NAME}_VERSION_TWEAK}" VERSION_AHEAD="${${VER_PROJ_NAME}_VERSION_AHEAD}" VERSION_GIT_SHA="${${VER_PROJ_NAME}_VERSION_GIT_SHA}" ) endmacro() ================================================ FILE: contrib/zhCN/D2RMH.ini ================================================ [main] ; D2LOD 路径 ; 如果该路径不包含游戏,则D2RMH会尝试从注册表读取安装目录 d2_path = . ; 字体路径 font_file_path = C:\Windows\Fonts\SimHei.ttf ; 文字字体大小,你可以: ; 1. 使用D2R自带的TTF,支持以下字体: ; 所有语言 (尤其是 koKR, ruRU, zhCN): ; blizzardglobal-v5_81.ttf(默认) ; zhTW: ; blizzardglobaltcunicode.ttf(默认), arfangxinshuh7c95b5_eb_t.ttf ; jaJP: ; bljap_v8_3.ttf(默认), ik4ll3.ttf, tbgdb_0pp.ttf ; enUS, deDE, plPL 以及其他拉丁语系语言: ; formal436bt.ttf(默认), exocetblizzardot-medium.otf, irisl.ttf, kodia.ttf, philosopher-bolditalic.ttf ; 留空会使用默认字体 ; 2. 使用TTF(扩展名为.ttf或.ttc), 以及D2/D2R的TBL/DC6字体 ; a. 对于TTF字体,在 `font_file_path` 里放路径 ; b. 对于TBL/DC6字体, 你需要放3个文件: base.dc6, base.tbl, base.pal(将data\global\palette\fechar\pal.dat复制出来改名), ; 并将任何一个文件的路径设置在 `font_file_path` 里, 你还可以在后面用 `|` 分隔追加原字体大小,如果和 `msg_font_size` 不一致 ; 举例: font_file_path = font24.dc6|24 font_size = 14 ; 消息列表字体大小 msg_font_size = 24 ; 可选语言: enUS,deDE,esES,frFR,itIT,koKR,plPL,esMX,jpJP,ptBR,ruRU,zhTW,zhCN ; 留空则使用 D2R 当前语言设置 language = [ui] ; 帧数限制,正数表示限制帧率,负数表示垂直同步交换间隔: ; 0=无限制并关闭垂直同步 ; -1=开启垂直同步,帧率和显示器刷新率相同 ; -2=开启垂直同步,帧率为显示器刷新率一半,依此类推 ; 注意:D2的逻辑是每秒25帧,所以这个值默认设为25 fps = 25 ; 0-游戏内小地图关闭时显示 ; 1-游戏内小地图打开时显示 ; 2-总是显示 show = 0 ; 如果不想地图覆盖到底部游戏UI条请设为0 draw_on_game_bar = 0 ; 当特定面板打开时隐藏本地图, 0x1FF=所有面板 ; 0x01-物品面板 ; 0x02-角色面板 ; 0x04-技能树面板 ; 0x08-系统菜单 ; 0x10-任务面板 ; 0x20-组队面板 ; 0x40-雇佣兵面板 ; 0x80-传送站面板 ; 0x100-技能选择列表 panel_mask = 0x1FF ; 路线指示画线方式 ; 0-显示一条短线和一个点(d2hackmap的方式) ; 1-直接连接到目的地 ; 2-通向目的地的行走路径(消耗更多CPU资源) line_style = 0 ; 0-左上 ; 1-右上 ; 2-正中 ; 注意: 如果设置为0或者1请认真阅读`map_area`的说明 position = 2 ; 限制显示范围(宽,高),相对于D2R窗口的大小,范围在0.0到1.0之间 ; 如果设为0.0则为动态大小,随当前地图大小改变 ; 可以只设置一个值,表示宽高都使用这个值 ; 如果map_centered为0则整个地图会在指定区域居中显示 map_area = 0.0 ; 地图缩放,范围为[1.0,4.0] ; 如果map_area不是动态大小,则超出显示范围的部分会被切去 ; 超出D2R窗口的部分也会被切去 scale = 2.0 ; 0-静态地图 ; 1-保持玩家显示在正中的动态地图 map_centered = 1 ; 整个地图层的透明通道(0-255) 0为完全透明 255为完全不透明 ; 和其他颜色的透明通道叠加,例如: ; 当颜色为 128,128,128,128(透明通道是128), alpha设为160时, ; 绘制颜色的透明通道就是: 128*160/255=80 alpha = 255 ; 相邻地图边界(点阵为单位) ; 可以设为负数表示寻找相邻地图循环深度 ; 例如 -1 表示只显示相邻地图 ; -2 还显示相邻地图的相邻地图 ; 设为0不显示相邻地图 ; 注意:设置较大的值,或设为负数但地图较大时会额外消耗一定的CPU和显存(2048最多大约消耗64M显存) ; 请关闭此选项如果感受到较大的系统性能损失 neighbour_map_bounds = -1 ; 可行走部分的颜色 (R,G,B) 每个颜色范围是0-255, 0,0,0,0表示透明 walkable_color = 20,20,20,50 ; 地图边缘的颜色 设为0,0,0,0隐藏边缘 edge_color = 128,128,128,240 ; 文字颜色 text_color = 255,255,255,180 ; 玩家方块的内外侧颜色 player_inner_color = 255,128,128,80 player_outer_color = 51,255,255,180 ; 指示线颜色 line_color = 204,204,204,144 ; 路点/传送门/箱子/任务/神殿/井的颜色 waypoint_color = 153,153,255,160 portal_color = 255,255,102,160 chest_color = 255,102,102,160 quest_color = 102,102,255,160 shrine_color = 255,51,178,160 well_color = 51,51,255,160 ; 怪物/金怪/npc的颜色 monster_color = 255,0,0,128 unique_monster_color = 192,166,130,204 npc_color = 160,160,160,160 ; 门的颜色 door_color = 80,255,80,180 ; 消息列表背景色 msg_bg_color = 1,1,1,128 ; 消息列表位置 (x,y,对齐) ; x和y的范围是[0.0,1.0], 对齐可以是0(左), 1(中), 2(右) msg_position = 0.95,0.25,2 ; 显示文字面板 (每秒更新),留空则不显示 ; 支持模板: ; {newline}/{n} 换行 ; {duration} 当前游戏持续时间,显示为hh:mm:ss ; {time} 当前系统时间: hh:mm:ss ; {difficulty} 当前难度 ; {act} 当前ACT ('ACT I' 到 'ACT V') ; {mapname} 当前地图名 ; {gamename} 当前游戏房间名 ; {gamepass} 当前游戏房间密码 ; {region} 游戏服务器所处区域代码 ; {season} 当前游戏赛季 text_panel_pattern = ;text_panel_pattern = {duration} ;text_panel_pattern = {difficulty}{n}{act}-{mapname}{n}{time}{n}{duration} ;text_panel_pattern = {gamename}{n}{gamepass}{n}{region} {season}{n}{difficulty}{n}{act}-{mapname}{n}{time}{n}{duration} ; 面板位置, 同 `msg_position` text_panel_position = 0.93,0.015,2 ; 显示玩家名 show_player_names = 1 ; 显示NPC名 show_npc_name = 1 ; 显示附近物件(目前显示神殿和井) show_objects = 1 ; 地图上物件(门除外)的最小尺寸(按点阵) object_size_minimal = 6 ; 显示附近的物品掉落,可以修改D2RMH_item.ini设定自己的掉落过滤表 show_items = 1 ; 显示附近的怪物 ; 0 - 不显示 ; 1 - 只有NPC/金怪/Boss会显示 ; 2 - 普通和仆从怪也显示 show_monsters = 2 ; 显示怪物名,怪物需要先在`show_monsters`中设置显示 ; 0 - 不显示 ; 1 - 只有NPC/金怪/Boss会显示 ; 2 - 普通和仆从怪也显示 show_monster_names = 1 ; 显示怪物强化类型,包括光环,更多配置请看[enchants]部分,怪物需要先在`show_monsters`中设置显示 ; 0 - 不显示 ; 1 - 只有NPC/金怪/Boss会显示 ; 2 - 普通和仆从怪也显示 show_monster_enchants = 1 ; 显示怪物的免疫,更多配置请看[enchants]部分,怪物需要先在`show_monsters`中设置显示 ; 0 - 不显示 ; 1 - 只有NPC/金怪/Boss会显示 ; 2 - 普通和仆从怪也显示 show_monster_immunities = 2 [enchants] ; 各种强化对应显示的字符串 ; 字符串可以用颜色来标识,写成'{n}'的形式, n和d2hackmap中一致: ; 0-文本颜色 1-红 2-绿 3-蓝 4-金 5-灰 6-黑 7-金2 ; 8-橙 9-黄 10-绿2 11-紫 12-绿3 13~15-白 ; 如果不设置默认为白色 extra_strong = 強壯 extra_fast = 快速 cursed = {2}詛咒 magic_resistant = 魔抗 fire_enchanted = {1}火強 ligntning_enchanted = {9}電強 cold_enchanted = {3}冰強 mana_burn = {3}法燃 teleportation = 傳送 spectral_hit = 幽靈 stone_skin = {4}石膚 multiple_shots = {12}多重 fanatic = {11}癲狂 berserker = {4}狂暴 ; 光环 might_aura = {4}力量 holyFire_aura = {1}聖火 blessedAim_aura = 瞄準 holyFreeze_aura = {3}聖冰 holyShock_aura = {9}聖雷 conviction_aura = {11}信念 fanaticism_aura = {5}狂熱 ; 免疫 physical_immunity = {4}物 magic_immunity = {8}魔 fire_immunity = {1}火 lightning_immunity = {9}電 cold_immunity = {3}冰 poison_immunity = {2}毒 [sound] ; 其他功能要用的声音文件设置 ; 支持WAVE文件,不以为.wav为扩展名的文件名将被视为系统事件声音 ; 常用系统事件声音: ; SystemAsterisk, SystemDefault, SystemExclamation, SystemHand ; 可以查看注册表项 'HKEY_CURRENT_USER\AppEvents\Schemes\Apps\.Default' 获取其他事件声音名 sound[1] = SystemDefault sound[2] = SystemAsterisk ; sound[3] = Beep.wav ================================================ FILE: contrib/zhCN/README.md ================================================ **README in other languages: [English](../../README.md)** # D2RMH Diablo II Resurrected 开图工具 # 免责声明 **D2RMH只从D2R读取内存,并没有注入代码、使用代码钩子以及写入内存, 但并不保证完全不会被封号,使用中出现任何问题概不负责。** # 版本更新 请看 [ChangeLog](../../doc/ChangeLog.md) # 需求依赖 * 本工具需要暗黑2 1.11, 1.11b, 1.12, 1.13c或1.13d版本,你可以[由此](https://archive.org/details/diablo-ii-1.13c-minimal.-7z)下载最精简版的1.13c # 使用 0. 病毒/木马检测警告: * Windows Defender容易误报,建议禁用或者加入白名单 * 虽然D2RMH可以通过大多数杀毒软件的检测,但也容易被误报,如果担心预编译文件有问题,那么可以参考下面的`如何编译`部分自行编译 1. 从 `Releases` 里下载最新发布版,或从 `Actions` 里下载最新的快照编译版(你需要登录GitHub) 2. 修改 D2RMH.ini(中文用户可以使用contrib/D2RMH_CN.ini的内容作为默认设置), 设置 `d2_path` 为你的D2 1.13c目录,也可以直接把`D2MRH.exe`和所有`*.ini`放到D2 1.13c目录里去 3. 运行 D2RMH.exe # Screenshots ![Screenshot 1](screenshots/screenshot_0.png) ![Screenshot 2](screenshots/screenshot_1.png) ![Screenshot 3](screenshots/screenshot_2.png) # 插件系统 * 插件加载自 `plugins` 目录里的所有 `.lua` 文件 * 如果想自己写插件请阅读 [文档](../../doc/Plugin.md) # TODO 请看 [TODO](../../doc/TODO.md) # 如何编译 ## 快速指引 * 安装 [cmake](https://www.cmake.org/) 并将 `cmake\bin` 路径添加到你的的环境变量 `PATH` 中,使得命令行下可以直接输入 `cmake` 使用 * 运行 `build_msvc2019.bat`, `build_msvc2022.bat`, `build_msys2_clang.bat` 或 `build_msys2_mingw.bat` 调用对应的编译器进行编译 注意: 你需要安装对应的编译器,如果是msys2编译,请参照下面的说明安装必要组件 ## 详细教程 ### MinGW GCC * 安装MSYS2(https://www.msys2.org), 打开MSYS2.exe,输入`pacman -Syu --noconfirm && pacman -S --noconfirm --needed make git mingw-w64-i686-toolchain mingw-w64-i686-cmake mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-cmake`安装必须的依赖组件 * 编译 D2RMH(64位): * 用 ucrt64.exe 打开命令行 * 克隆D2RMH的源代码: `git clone https://github.com/soarqin/D2RMH` * 输入 `cd D2RMH && cmake -Bbuild -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_STATIC_CRT=ON` * 然后 `make -Cbuild D2RMH` 就能在 `build/bin` 里生成编译好的exe * 编译 d2mapapi-piped(32位): * 用 mingw32.exe 打开命令行 * 进入 D2RMH 所在目录 * 输入 `cmake -Bbuild_d2mapapi -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_STATIC_CRT=ON d2mapapi` * 然后 `make -Cbuild_d2mapapi d2mapapi-piped` 就能在 `build/bin` 里生成编译好的exe ### MSYS2 Clang * 和 MinGW GCC 大致相同,除了以下改变: * `mingw-w64-i686-toolchain`->`mingw-w64-clang-i686-toolchain` * `mingw-w64-i686-cmake`->`mingw-w64-clang-i686-cmake` * `mingw-w64-ucrt-x86_64-toolchain`->`mingw-w64-clang-x86_64-toolchain` * `mingw-w64-ucrt-x86_64-cmake`->`mingw-w64-clang-x86_64-cmake` * `ucrt64.exe`->`clang64.exe` * `mingw32.exe`->`clang32.exe` ### Microsoft Visual Studio 2019/2022 * 安装Visual Studio 2019或2022社区版(或者你有专业/企业版也可以) * 解压下载的源代码文件,或者用git来clone仓库: `git clone https://github.com/soarqin/D2RMH` 注意: 你需要 [Git for windows](https://git-scm.com/download/win) * 编译 D2RMH(64位): * (Visual Studio 2019) 输入 `cmake -Bbuild -G "Visual Studio 16 2019" -A x64 -DUSE_STATIC_CRT=ON` (Visual Studio 2022) 输入 `cmake -Bbuild -G "Visual Studio 17 2022" -A x64 -DUSE_STATIC_CRT=ON` * 然后你可以选择: * 输入 `cmake --build build --config Release --target D2RMH` * 打开 `build\D2RMH.sln` 编译目标 `D2RMH` * 编译好的exe在 `build\bin` 目录 * 编译 d2mapapi-piped(32位): * (Visual Studio 2019) 输入 `cmake -Bbuild_d2mapapi -G "Visual Studio 16 2019" -A Win32 -DUSE_STATIC_CRT=ON d2mapapi` (Visual Studio 2022) 输入 `cmake -Bbuild_d2mapapi -G "Visual Studio 17 2022" -A Win32 -DUSE_STATIC_CRT=ON d2mapapi` * 然后你可以选择: * 打开 `cmake --build build_d2mapapi --config Release --target d2mapapi_piped` * 打开 `build_d2mapapi\d2mapapi.sln` 编译目标 `d2mapapi_piped` * 编译好的exe在 `build_d2mapapi\bin` 目录 # 鸣谢 * [d2mapapi_mod](https://github.com/soarqin/d2mapapi_mod) 修改自 [d2mapapi](https://github.com/jcageman/d2mapapi). * 想法以及内存地址来自 [MapAssist](https://github.com/misterokaygo/MapAssist). * [Handmade Math](https://github.com/HandmadeMath/Handmade-Math) 处理向量和矩阵运算 * [glad](https://glad.dav1d.de) 加载 OpenGL(Core)/WGL 函数 * [inih](https://github.com/benhoyt/inih) 读取 ini 文件 * [JSON for Modern C++](https://github.com/nlohmann/json) 读取 JSON 文件 * [CascLib](https://github.com/ladislav-zezula/CascLib) 从D2R读取Casc存储 * [stb](https://github.com/nothings/stb), 使用了stb_truetype和stb_rect_pack ================================================ FILE: contrib/zhCN/plugins/chicken_life.lua ================================================ local text = create_text_list("default") local chicken_life = function() local player = get_player() if player and player.map ~= 1 and player.map ~= 40 and player.map ~= 75 and player.map ~= 103 and player.map ~= 109 then if player.stats[7] < player.stats[8] * 0.3 then kill_process() end end end function chicken_life_toggle(on) if on then text:add('生命安全线保护功能已开启 开关热键: \'Ctrl+/\'', 5000, -1) else text:add('生命安全线保护功能已关闭 开关热键: \'Ctrl+/\'', 5000, -1) end end register_plugin("Ctrl+/", false, 250, chicken_life, chicken_life_toggle) ================================================ FILE: contrib/zhCN/plugins/town_portal_check.lua ================================================ local text = create_text_list("default") local town_portal_check = function() -- game fully loaded? if not get_skill(0) then return end local quantity local portal_book = get_skill(220) if portal_book then quantity = portalbook.quantity else quantity = 0 end if quantity < 3 then text:add(string.format("\x0B警告!回城卷轴数量不足: %d", quantity), 1500, 0) end end register_plugin(1000, town_portal_check) ================================================ FILE: copy_dist.bat ================================================ @echo off cmake -E copy_if_different bin\D2RMH.ini bin\D2RMH_gamedata.ini bin\D2RMH_item.ini build\%BUILD_DIR%\dist\ cmake -E copy_directory bin\plugins build\%BUILD_DIR%\dist\plugins cmake -E copy_directory doc build\%BUILD_DIR%\dist\doc cmake -E copy_if_different README.md LICENSE build\%BUILD_DIR%\dist\ cmake -E copy_if_different doc\LICENSE.lua54 build\%BUILD_DIR%\dist\doc\ cmake -E copy_if_different deps\CascLib\LICENSE build\%BUILD_DIR%\dist\doc\LICENSE.CascLib cmake -E copy_if_different deps\inih\LICENSE.txt build\%BUILD_DIR%\dist\doc\LICENSE.inih cmake -E copy_if_different deps\sol3\LICENSE.txt build\%BUILD_DIR%\dist\doc\LICENSE.sol3 cmake -E copy_if_different d2mapapi\LICENSE build\%BUILD_DIR%\dist\doc\LICENSE.d2mapapi_mod cmake -E copy_if_different d2mapapi\json\LICENSE.MIT build\%BUILD_DIR%\dist\doc\LICENSE.nlohmann_json pushd build\%BUILD_DIR%\dist >NUL cmake -E tar cf D2RMH-snapshot.zip --format=zip D2RMH.exe d2mapapi_piped.exe D2RMH.ini D2RMH_gamedata.ini D2RMH_item.ini README.md LICENSE doc plugins popd >NUL ================================================ FILE: d2mapapi/.gitignore ================================================ # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app ================================================ FILE: d2mapapi/.gitrepo ================================================ ; DO NOT EDIT (unless you know what you are doing) ; ; This subdirectory is a git "subrepo", and this file is maintained by the ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme ; [subrepo] remote = git@github.com:soarqin/d2mapapi_mod.git branch = master commit = f61d05244f323409aa48b326033639326d7c285c parent = 5ae01c80237b531a6a17de30e7c55907f6d30339 method = rebase cmdver = 0.4.3 ================================================ FILE: d2mapapi/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13) project(d2mapapi VERSION 1.3.0) if(MSVC) set(CMAKE_CXX_STANDARD 20) else() set(CMAKE_CXX_STANDARD 17) endif() if ("${CMAKE_SOURCE_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") include(CustomCompilerOptions) fix_compile_flags() fix_release_flags() add_static_runtime_option() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 4) add_subdirectory(simphttp) add_library(d2mapapi STATIC EXCLUDE_FROM_ALL crc32.h collisionmap.cpp collisionmap.h mapdata.cpp mapdata.h pathfinder.cpp pathfinder.h d2map.cpp d2map.h d2ptrs.h d2structs.h offset.cpp offset.cpp session.cpp session.h) target_include_directories(d2mapapi PUBLIC . PRIVATE json) set_target_properties(d2mapapi PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) add_executable(d2mapapi_gen_image EXCLUDE_FROM_ALL genimage.cpp) target_compile_definitions(d2mapapi_gen_image PRIVATE D2MAPAPI_VERSION="${PROJECT_VERSION}") target_link_libraries(d2mapapi_gen_image d2mapapi shlwapi) if(MSVC) target_link_options(d2mapapi_gen_image PRIVATE /ENTRY:wmainCRTStartup) else() target_link_options(d2mapapi_gen_image PRIVATE -municode) endif() set_target_properties(d2mapapi_gen_image PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) add_executable(d2mapapi_httpd EXCLUDE_FROM_ALL httpd.cpp) target_compile_definitions(d2mapapi_httpd PRIVATE D2MAPAPI_VERSION="${PROJECT_VERSION}") target_link_libraries(d2mapapi_httpd d2mapapi simphttp) if(MSVC) target_link_options(d2mapapi_httpd PRIVATE /ENTRY:wmainCRTStartup) else() target_link_options(d2mapapi_httpd PRIVATE -municode) endif() set_target_properties(d2mapapi_httpd PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) add_executable(d2mapapi_piped WIN32 EXCLUDE_FROM_ALL piped.cpp) target_compile_definitions(d2mapapi_piped PRIVATE D2MAPAPI_VERSION="${PROJECT_VERSION}") target_include_directories(d2mapapi_piped PRIVATE json) target_link_libraries(d2mapapi_piped d2mapapi) if(MSVC) target_link_options(d2mapapi_piped PRIVATE /ENTRY:wmainCRTStartup) else() target_link_options(d2mapapi_piped PRIVATE -municode) endif() set_target_properties(d2mapapi_piped PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) endif() add_library(d2mapapi_pipehost STATIC EXCLUDE_FROM_ALL collisionmap.cpp collisionmap.h pathfinder.cpp pathfinder.h pipehost.cpp pipehost.h) target_include_directories(d2mapapi_pipehost PUBLIC . PRIVATE json) set_target_properties(d2mapapi_pipehost PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # this is a test program for d2mapapi_pipehost functions add_executable(d2mapapi_host EXCLUDE_FROM_ALL host.cpp) target_link_libraries(d2mapapi_host d2mapapi_pipehost) set_target_properties(d2mapapi_host PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) ================================================ FILE: d2mapapi/LICENSE ================================================ MIT License Copyright (c) 2020 jcageman Copyright (c) 2021-2022 Soar Qin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: d2mapapi/README.md ================================================ # d2mapapi_mod **d2mapapi mod, original from [jcageman/d2mapapi](https://github.com/jcageman/d2mapapi)** # Projects * `d2mapapi`: basic library for generating map data * `d2mapapi_gen_image`: generate map into image files (support PNG, BMP, TGA and JPG) * `d2mapapi_httpd`: httpd server, depends on `d2mapapi` * `d2mapapi_piped`: pipe query server, depends on `d2mapapi` * `d2mapapi_pipehost`: pipe querying library for communicating with `d2mapapi_piped` * `d2mapapi_host`: example and test project for do pipe query using `d2mapapi_pipehost` # Usage for `d2mapapi_gen_image` * command line: `d2mapapi_gen_image [d2_path] ` * `d2_path` is an optional parameter, which set legacy Diablo II installation path, `d2mapapi_httpd` will search registry for installation path if this parameter is absent or does not point to a correct installed game path. * supported Diablo II Legacy client version: `1.11`, `1.11b`, `1.12`, `1.13c`, `1.13d` * `seed`, `difficulty`, `map`: d2 map seed, game difficualty and map area id * `image filename`: image filename, format is determined by file extension, supported: `.png`, `.tga`, `.bmp` and `.jpg` # Usage for `d2mapapi_httpd` * command line: `d2mapapi_httpd [d2_path]` * `d2_path` is an optional parameter, which set legacy Diablo II installation path, `d2mapapi_httpd` will search registry for installation path if this parameter is absent or does not point to a correct installed game path. * supported Diablo II Legacy client version: `1.11`, `1.11b`, `1.12`, `1.13c`, `1.13d` * http server is listen on port `8000` * http url is in RESTful form: * `http://localhost:8000/{seed}/{difficulty}/{map}/{indentation}` * `{seed}`: d2 map seed * `{difficulty}`: game difficulty * 0: normal * 1: nightmare * 2: hell * `{map}`: map area id * `{indentation}`: optional field, JSON indentation spaces. JSON will be compactly encoded if this is not set * returns the JSON data described below # Usage for `d2mapapi_piped` * command line: `d2mapapi_httpd [d2_path]` * `d2_path` is an optional parameter, which set legacy Diablo II installation path, `d2mapapi_httpd` will search registry for installation path if this parameter is absent or does not point to a correct installed game path. * supported Diablo II Legacy client version: `1.11`, `1.11b`, `1.12`, `1.13c`, `1.13d` * use `PipedChildProcess::start()` to run the child process, you can pass `d2_path` as its second parameter, or pass `nullptr` for reading from registry. * use `PipedChildProcess::queryMapRaw()` to get the encoded JSON(described below) for map data. * use `PipedChildProcess::queryMap()` to get the decoded map data. * Note: you should delete the returned `CollisionMap*` pointer while it is no more needed. # JSON structure for map data * encoded JSON data are generated by `d2mapapi::CollisionMap::encode()` and returned by `d2mapapi_httpd` and `d2mapapi_piped` * use `d2mapapi::CollisionMap::CollisionMap(const std::string&)` to build map data from encoded JSON * ``` { # map id "id": 1, # map offset in whole act "offset": ["x": 5520, "y": 5880], # map size "size": ["width": 280, "height": 200], # crop area of map data "crop": ["x0": 0, "x1": 280, "y0": 0, "y1": 200], # mapData is an encoded array with area in [x0, y0]-[x1, y1] # check description below to see how are map data encoded "mapData": [1,5,1,-1, 2,3,2,-1, 1,5,1,-1], # map exits "exits": { # exit to map id 2 "2": { # this is not a portal (so that you can walk between the map areas) "isPortal": false, # exit offsets in list, sometimes there can be multiple exits to another map area # note: the offsets are not always correct while `isPortal` is false, because they are calculated by an inaccurate algorithm "offsets": [{"x": 5672, "y": 5880}] } }, # npcs on map "npcs": { # npc id is 147, with offset list so that there can be multiple npcs in the same id "147": [{"x": 5615, "y": 5967}] }, # objects on map "objects": { # object id is 147, with offset list so that there can be multiple objects in the same id "119": [{"x": 5634, "y": 5889}] } } ``` * map data are encoded using a simple run length encoding to save memory space -1 ends of a row Given this small map: ``` [1,5,1,-1, 2,3,2,-1, 1,5,1,-1] ``` Generates the following map where `X` is collision and `.` is open space ``` X.....X XX...XX X.....X ``` # Build * Just use [cmake](https://cmake.org) to build * Note: `d2mapapi`, `d2mapapi_httpd`, `d2mapapi_piped` and `d2mapapi_gen_image` can only be compiled in 32-bit. # Credits * Core functions modified from [d2mapapi](https://github.com/jcageman/d2mapapi). * [JSON for Modern C++](https://github.com/nlohmann/json) for processing JSON files. * [libuv](https://github.com/libuv/libuv) and [llhttp](https://github.com/nodejs/llhttp), for httpd server. * [stb](https://github.com/nothings/stb), stb_image_write is used to write to image files. ================================================ FILE: d2mapapi/cmake/CustomCompilerOptions.cmake ================================================ macro(fix_compile_flags) if(MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") endif() endmacro() macro(fix_release_flags) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s -flto") set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} -s -flto") set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -s -flto") set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL} -s -flto") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -flto") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") endif() if(MSVC) add_compile_options( $<$:/GL> $<$:/GL> ) add_link_options( $<$:/LTCG> $<$:/LTCG> ) endif() endmacro() macro(add_static_runtime_option) option(USE_STATIC_CRT "Use static C runtime" OFF) if(USE_STATIC_CRT) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static -static-libgcc -static-libstdc++") elseif(MSVC) set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO ) foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") set(${CompilerFlag} "${${CompilerFlag}}" CACHE STRING "msvc compiler flags" FORCE) endforeach() add_compile_options( $<$:/MT> $<$:/MTd> $<$:/MT> $<$:/MT> $<$:/MT> ) endif() endif() endmacro() ================================================ FILE: d2mapapi/collisionmap.cpp ================================================ #include "collisionmap.h" #include #include namespace d2mapapi { CollisionMap::CollisionMap(std::string_view str) { decode(str); } NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Point, x, y) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Size, width, height) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Rect, x0, y0, x1, y1) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Exit, offsets, isPortal) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CollisionMap, id, offset, size, crop, mapData, exits, npcs, objects, pathData) template void to_json(nlohmann::json &j, const std::map &objs) { for (auto &p: objs) { j[std::to_string(p.first)] = p.second; } } template void from_json(const nlohmann::json &j, std::map &objs) { for (auto &[key, value]: j.items()) { nlohmann::adl_serializer::from_json(value, objs[uint32_t(strtoul(key.c_str(), nullptr, 0))]); } } std::string CollisionMap::encode(bool encPathData, int indentation) const { if (!built) { return {}; } nlohmann::json j = *this; if (!encPathData) { j["pathData"].clear(); } return j.dump(indentation > 0 ? indentation : -1); } void CollisionMap::decode(std::string_view str) { auto j = nlohmann::json::parse(str, nullptr, false); if (j.empty()) { built = false; errorString = "Wrong input."; return; } auto ite = j.find("error"); if (ite != j.end()) { built = false; errorString = ite->get(); return; } try { from_json(j, *this); } catch (const std::exception &e) { built = false; errorString = e.what(); return; } built = true; errorString.clear(); if (pathData.empty()) { return; } auto sz = pathData.size(); size_t currSize = 0; path.clear(); path.reserve(((crop.x1 - crop.x0) / 5) * ((crop.y1 - crop.y0) / 5)); for (size_t i = 1; i < sz; i += 2) { currSize += pathData[i]; path.resize(currSize, pathData[i - 1]); } } } ================================================ FILE: d2mapapi/collisionmap.h ================================================ #pragma once #include #include #include #include namespace d2mapapi { struct Point { int x = 0; int y = 0; }; struct Size { int width = 0; int height = 0; }; struct Rect { int x0, y0, x1, y1; }; struct Exit { std::vector offsets; bool isPortal = false; }; class CollisionMap { public: explicit CollisionMap(unsigned int areaId): id(areaId) {} explicit CollisionMap(std::string_view str); virtual ~CollisionMap() = default; [[nodiscard]] std::string encode(bool encPathData = false, int indentation = 0) const; void decode(std::string_view str); template inline bool extractCellData(T *output, int width, int height, int originX, int originY, T nonwalkableVal, T walkableVal, T edgeVal = T()) const { auto w = std::max(crop.x1 - crop.x0, 0); auto h = std::max(crop.y1 - crop.y0, 0); if (originX + w > width || originY + h > height) { return false; } int x = 0, y = 0; int index = originY * width + originX; bool walkable = false; bool hasEdge = edgeVal != T(); for (int v: mapData) { if (v < 0) { if (++y >= h) { break; } x = 0; index = (originY + y) * width + originX; walkable = false; continue; } v = std::min(int(v), w - x); if (!walkable) { std::fill_n(output + index, v, nonwalkableVal); if (hasEdge && y > 0) { T *ptr = output + index - width; for (int z = v; z; z--, ptr++) { if (*ptr == walkableVal) { *ptr = edgeVal; } } } index += v; } else { if (hasEdge && y + 1 == crop.y1 && crop.y1 < height) { std::fill_n(output + index, v, edgeVal); index += v; } else { int z = v; if (hasEdge) { *(output + index) = x > 0 ? edgeVal : walkableVal; index++; z -= 2; } if (z >= 0) { if (hasEdge && y > 0) { for (; z; z--) { *(output + index) = *(output + index - width) == nonwalkableVal ? edgeVal : walkableVal; index++; } } else { std::fill_n(output + index, z, walkableVal); index += z; } if (hasEdge) { *(output + index) = x + v < w ? edgeVal : walkableVal; index++; } } } } x += v; walkable = !walkable; } return true; } bool built = false; std::string errorString; unsigned int id = 0; Point offset = {0, 0}; Size size = {0, 0}; /* Collission maps are cropped in rect [crop.x0, crop.y0] to [crop.x1, crop.y1] relative to [offset.x, offset.y] */ Rect crop = {-1, -1, -1, -1}; /* Collision maps are encoded using a simple run length encoding to save memory space * -1 ends of a row * Given this small map: * [1,5,1,-1, * 2,3,2,-1, * 1,5,1,-1] * It would generate the following map where `X` is collision and `.` is open space * X.....X * XX...XX * X.....X */ std::vector mapData; std::map exits; std::map> npcs; std::map> objects; std::vector path; std::vector pathData; }; } ================================================ FILE: d2mapapi/crc32.h ================================================ #include #include namespace crc { // Small implementation of std::array, needed until constexpr // is added to the function 'reference operator[](size_type)' template struct array { T m_data[N]; using value_type = T; using reference = value_type &; using const_reference = const value_type &; using size_type = std::size_t; // This is NOT constexpr in std::array until C++17 constexpr reference operator[](size_type i) noexcept { return m_data[i]; } constexpr const_reference operator[](size_type i) const noexcept { return m_data[i]; } constexpr size_type size() const noexcept { return N; } }; // Generates CRC-32 table, algorithm based from this link: // http://www.hackersdelight.org/hdcodetxt/crc.c.txt constexpr auto gen_crc32_table() { constexpr auto num_bytes = 256; constexpr auto num_iterations = 8; constexpr auto polynomial = 0xEDB88320; auto crc32_table = array{}; for (auto byte = 0u; byte < num_bytes; ++byte) { auto crc = byte; for (auto i = 0; i < num_iterations; ++i) { auto mask = -(crc & 1); crc = (crc >> 1) ^ (polynomial & mask); } crc32_table[byte] = crc; } return crc32_table; } // Stores CRC-32 table and softly validates it. static constexpr auto crc32_table = gen_crc32_table(); static_assert( crc32_table.size() == 256 && crc32_table[1] == 0x77073096 && crc32_table[255] == 0x2D02EF8D, "gen_crc32_table generated unexpected result." ); constexpr auto crc32(const void *in, std::size_t sz) { const auto *data = static_cast(in); auto crc = 0xFFFFFFFFu; for (size_t i = 0; i < sz; ++i) { crc = crc32_table[(crc ^ data[i]) & 0xFFu] ^ (crc >> 8); } return ~crc; } } ================================================ FILE: d2mapapi/d2map.cpp ================================================ #include "d2map.h" #include "d2ptrs.h" #include "offset.h" #include #include namespace d2mapapi { d2client_struct D2Client; const char *d2Init(const wchar_t *dir) { wchar_t szPath[MAX_PATH] = {0}; GetCurrentDirectoryW(MAX_PATH, szPath); if (dir[0] != 0 && dir[lstrlenW(dir) - 1] != '\\') { wchar_t dira[MAX_PATH]; lstrcpyW(dira, dir); lstrcatW(dira, L"\\"); SetCurrentDirectoryW(dira); } else { SetCurrentDirectoryW(dir); } memset(&D2Client, 0, sizeof(d2client_struct)); if (!defineOffsets()) { return "Diablo II Legacy: Failed to load DLLs!"; } auto version = getD2Version(); *p_STORM_MPQHashTable = 0; if (version < D2_113c) { D2Client.u112.dwInit = 1; D2Client.u112.fpInit = (uint32_t)D2ClientInterface; } else { D2Client.u113.dwInit = 1; D2Client.u113.fpInit = (uint32_t)D2ClientInterface; } FOG_10021("D2"); FOG_10101(1, 0); FOG_10089(1); if (!FOG_10218()) { return "Diablo II Legacy: Initialize Failed!"; } if (!D2WIN_10086() || !D2WIN_10005(0, 0, 0, &D2Client)) { return "Diablo II Legacy: Couldn't load MPQ files.\nPlease make sure you have a full install of Diablo II and copy the D2XMUSIC.MPQ and D2XVIDEO.MPQ from the Expansion CD"; } D2LANG_Init(0, "ENG", 0); if (!D2COMMON_InitDataTables(0, 0, 0)) { return "Diablo II Legacy: Couldn't initialize sqptDataTable!"; } D2CLIENT_InitGameMisc(); SetCurrentDirectoryW(szPath); return nullptr; } Level *__fastcall getLevel(Act *act, uint32_t levelno) { auto d2Ver = getD2Version(); for (Level *pLevel = act->pMisc(d2Ver)->pLevelFirst(d2Ver); pLevel; pLevel = pLevel->pNextLevel(d2Ver)) if (pLevel->dwLevelNo(d2Ver) == levelno) return (Level*)pLevel; return D2COMMON_GetLevel(act->pMisc(d2Ver), levelno); } #if defined(_MSC_VER) void __declspec(naked) D2CLIENT_InitGameMisc(void) { __asm { PUSH ECX PUSH EBP PUSH ESI PUSH EDI JMP D2CLIENT_InitGameMisc_I RETN } } #else void __attribute__((naked)) D2CLIENT_InitGameMisc() { asm volatile ( "push %%ecx\n" "push %%ebp\n" "push %%esi\n" "push %%edi\n" "jmp *%0\n" "ret" : : "r"(D2CLIENT_InitGameMisc_I) ); } #endif uint32_t D2ClientInterface() { if (getD2Version() < D2_113c) { return D2Client.u112.dwInit; } return D2Client.u113.dwInit; } } ================================================ FILE: d2mapapi/d2map.h ================================================ #pragma once #include "d2structs.h" namespace d2mapapi { #define ArraySize(x) (sizeof(x) / sizeof(x[0])) /* return NULL if no error, * otherwise return error message */ const char *d2Init(const wchar_t *dir); Level *__fastcall getLevel(Act *act, uint32_t levelno); void D2CLIENT_InitGameMisc(); uint32_t D2ClientInterface(); } ================================================ FILE: d2mapapi/d2ptrs.h ================================================ #pragma once #include "d2structs.h" #if defined(_DEFINE_VARS) #define D2EXTERN #else #define D2EXTERN extern #endif namespace d2mapapi { D2EXTERN uint32_t *p_STORM_MPQHashTable; D2EXTERN uint32_t D2CLIENT_LoadAct_1; D2EXTERN uint32_t D2CLIENT_LoadAct_2; D2EXTERN void (__stdcall* D2CLIENT_InitGameMisc_I)(uint32_t Dummy1, uint32_t Dummy2, uint32_t Dummy3); D2EXTERN void (__stdcall* D2COMMON_AddRoomData)(Act * ptAct, int LevelId, int Xpos, int Ypos, Room1 *pRoom); D2EXTERN void (__stdcall* D2COMMON_RemoveRoomData)(Act * ptAct, int LevelId, int Xpos, int Ypos, Room1 *pRoom); D2EXTERN Level * (__fastcall* D2COMMON_GetLevel)(ActMisc * pMisc, uint32_t dwLevelNo); D2EXTERN void (__stdcall* D2COMMON_InitLevel)(Level * pLevel); D2EXTERN Act* (__stdcall* D2COMMON_LoadAct)(uint32_t ActNumber, uint32_t Seed, uint32_t Unk, void *pGame, uint32_t Difficulty, void *pMempool, uint32_t TownLevelId, uint32_t Func_1, uint32_t Func_2); D2EXTERN void (__stdcall* D2COMMON_UnloadAct)(Act * pAct); D2EXTERN void (__fastcall* FOG_10021)(const char *szProg); D2EXTERN uint32_t (__fastcall* FOG_10101)(uint32_t Dummy1, uint32_t Dummy2); D2EXTERN uint32_t (__fastcall* FOG_10089)(uint32_t Dummy1); D2EXTERN uint32_t (__fastcall* FOG_10218)(void); D2EXTERN uint32_t (__fastcall* D2WIN_10086)(void); D2EXTERN uint32_t (__fastcall* D2WIN_10005)(uint32_t Dummy1, uint32_t Dummy2, uint32_t Dummy3, d2client_struct * pD2Client); D2EXTERN uint32_t (__fastcall* D2LANG_Init)(uint32_t Dummy1, const char *_2, uint32_t Dummy3); D2EXTERN uint32_t (__stdcall* D2COMMON_InitDataTables)(uint32_t Dummy1, uint32_t Dummy2, uint32_t Dummy3); } ================================================ FILE: d2mapapi/d2structs.h ================================================ #pragma once #include "offset.h" #include namespace d2mapapi { struct UnitAny; struct Room1_111; struct Room1_112; struct Room1_113; struct Room2_111; struct Room2_112; struct Room2_113; struct Level111; struct Level112; struct Level113; struct Act111; struct Act112; struct Act113; struct ActMisc111; struct ActMisc112; struct ActMisc113; union RoomTile; union PresetUnit; union Room1; union Room2; union Level; union Act; union ActMisc; #pragma pack(push) #pragma pack(1) struct d2client_struct_112 { uint32_t dwInit; //0x00 uint8_t _1[0x20C-4]; //0x04 uint32_t fpInit; //0x20C }; struct d2client_struct_113 { uint32_t dwInit; //0x00 uint8_t _1[0x20D - 4]; //0x04 uint32_t fpInit; //0x20D }; struct CollMap { uint32_t dwPosGameX; //0x00 uint32_t dwPosGameY; //0x04 uint32_t dwSizeGameX; //0x08 uint32_t dwSizeGameY; //0x0C uint32_t dwPosRoomX; //0x10 uint32_t dwPosRoomY; //0x14 uint32_t dwSizeRoomX; //0x18 uint32_t dwSizeRoomY; //0x1C uint16_t *pMapStart; //0x20 uint16_t *pMapEnd; //0x24 }; struct RoomTile111 { uint32_t _1; Room2 *pRoom2; //+04 RoomTile *pNext; //+08 uint32_t *nNum; //+0c }; struct RoomTile112 { uint32_t *nNum; //0x00 Room2 *pRoom2; //0x04 uint32_t _1[2]; //0x08 RoomTile *pNext; //0x10 }; struct RoomTile113 { Room2 *pRoom2; //0x00 RoomTile *pNext; //0x04 uint32_t _2[2]; //0x08 uint32_t *nNum; //0x10 }; struct PresetUnit111 { uint32_t _1[2]; uint32_t dwPosY; //+08 uint32_t dwTxtFileNo; //+0c uint32_t _2[1]; PresetUnit *pPresetNext; //+1c uint32_t dwPosX; //+20 uint32_t dwType; //+24 }; struct PresetUnit112 { uint32_t dwTxtFileNo; //0x00 uint32_t _1[2]; //0x04 uint32_t dwPosX; //0x0C uint32_t _2; //0x10 uint32_t dwPosY; //0x14 PresetUnit *pPresetNext; //0x18 uint32_t dwType; //0x1C }; struct PresetUnit113 { uint32_t _1; //0x00 uint32_t dwTxtFileNo; //0x04 uint32_t dwPosX; //0x08 PresetUnit *pPresetNext; //0x0C uint32_t _3; //0x10 uint32_t dwType; //0x14 uint32_t dwPosY; //0x18 }; struct Level111 { uint32_t _1; uint32_t dwPosX; uint32_t dwPosY; uint32_t dwSizeX; uint32_t dwSizeY; uint32_t dwLevelNo; //+14 uint32_t _1a[120]; uint32_t dwSeed[2]; //+1f8 uint32_t _2[1]; Room2 *pRoom2First; //+204 ActMisc *pMisc; //+208 uint32_t _3[8]; Level *pNextLevel; //+22c }; struct Level112 { uint8_t _1[0x50]; //0x00 uint32_t dwSeed[2]; //0x50 uint32_t _2; //0x58 Level *pNextLevel; //0x5C uint32_t _56; //0x60 ActMisc *pMisc; //0x64 uint32_t _3; //0x68 uint32_t dwPosX; //0x6C uint32_t dwPosY; //0x70 uint32_t dwSizeX; //0x74 uint32_t dwSizeY; //0x78 uint32_t _4[6]; //0x7C uint32_t dwLevelNo; //0x94 uint32_t _5[0x61]; //0x98 Room2 *pRoom2First; //0x21C }; struct Level113 { uint32_t _1[4]; //0x00 Room2 *pRoom2First; //0x10 uint32_t _2[2]; //0x14 uint32_t dwPosX; //0x1C uint32_t dwPosY; //0x20 uint32_t dwSizeX; //0x24 uint32_t dwSizeY; //0x28 uint32_t _3[96]; //0x2C Level *pNextLevel; //0x1AC uint32_t _4; //0x1B0 ActMisc *pMisc; //0x1B4 uint32_t _5[6]; //0x1BC uint32_t dwLevelNo; //0x1D0 }; struct Room2_111 { RoomTile *pRoomTiles; // uint32_t _1[1]; uint32_t dwPresetType; //+8 uint32_t _2[1]; uint32_t dwRoomsNear; //+10 uint32_t _2a[2]; Level *pLevel; //+1c uint32_t dwPosX; //+20 uint32_t dwPosY; //+24 uint32_t dwSizeX; //+28 uint32_t dwSizeY; //+2c Room2 **pRoom2Near; //+30 PresetUnit *pPreset; //+34 Room2 *pRoom2Next; //+38 uint32_t _4[38]; uint32_t dwSeed[2]; //+d4 uint32_t *pType2Info; //+dc uint32_t _5[2]; Room1 *pRoom1; //+e8 }; struct Room2_112 { Level *pLevel; //0x00 uint32_t _1; //0x04 uint32_t dwRoomsNear; //0x08 RoomTile *pRoomTiles; //0x0C Room2 **pRoom2Near; //0x10 uint32_t _3[6]; //0x14 uint32_t dwPosX; //0x2C uint32_t dwPosY; //0x30 uint32_t dwSizeX; //0x34 uint32_t dwSizeY; //0x38 uint32_t *pType2Info; //0x3C uint32_t _4[0x20]; //0x40 uint32_t dwPresetType; //0xC0 PresetUnit *pPreset; //0xC4 uint32_t _5[0x3]; //0xC8 Room2 *pRoom2Next; //0xD4 Room1 *pRoom1; //0xD8 }; struct Room2_113 { uint32_t _1[2]; //0x00 Room2 **pRoom2Near; //0x08 uint32_t _2[6]; //0x0C Room2 *pRoom2Next; //0x24 uint32_t dwRoomFlags; //0x28 uint32_t dwRoomsNear; //0x2C Room1 *pRoom1; //0x30 uint32_t dwPosX; //0x34 uint32_t dwPosY; //0x38 uint32_t dwSizeX; //0x3C uint32_t dwSizeY; //0x40 uint32_t _3; //0x44 uint32_t dwPresetType; //0x48 RoomTile *pRoomTiles; //0x4C uint32_t _4[2]; //0x50 Level *pLevel; //0x58 PresetUnit *pPreset; //0x5C }; struct Room1_111 { uint32_t dwSeed[2]; //+00 uint32_t dwXStart; //+08 uint32_t dwYStart; //+0c uint32_t dwXSize; //+10 uint32_t dwYSize; //+14 uint32_t dwXStart2; //+18 uint32_t dwYStart2; //+1c uint32_t dwXSize2; //+20 uint32_t dwYSize2; //+24 uint32_t _2[3]; Room1 **pRoomsNear; //+34 Room2 *pRoom2; //+38 UnitAny *pUnitFirst; //+3c uint32_t _3[8]; CollMap *Coll; uint32_t _4[4]; Room1 *pRoomNext; // +74 uint32_t _5; uint32_t dwRoomsNear; //+7c }; struct Room1_112 { Room1 **pRoomsNear; //0x00 uint32_t _1[2]; //0x04 uint32_t dwSeed[2]; //0x0C uint32_t _2; //0x14 uint32_t dwXStart; //0x18 uint32_t dwYStart; //0x1C uint32_t dwXSize; //0x20 uint32_t dwYSize; //0x24 uint32_t _3[0x4]; //0x28 Room1 *pRoomNext; //0x38 uint32_t _4; //0x3C UnitAny *pUnitFirst; //0x40 uint32_t _5[3]; //0x44 CollMap *Coll; //0x50 uint32_t _6[0x7]; //0x54 Room2 *pRoom2; //0x70 uint32_t _7; //0x74 uint32_t dwRoomsNear; //0x78 }; struct Room1_113 { Room1 **pRoomsNear; //0x00 uint32_t _1[3]; //0x04 Room2 *pRoom2; //0x10 uint32_t _2[3]; //0x14 CollMap *Coll; //0x20 uint32_t dwRoomsNear; //0x24 uint32_t _3[9]; //0x28 uint32_t dwPosX; //0x4C uint32_t dwPosY; //0x50 uint32_t dwSizeX; //0x54 uint32_t dwSizeY; //0x58 uint32_t _4[6]; //0x5C UnitAny *pUnitFirst; //0x74 uint32_t _5; //0x78 Room1 *pRoomNext; //0x7C }; struct ActMisc111 { uint32_t _1[33]; Act *pAct; //+84 uint32_t dwBossTombLvl; //+88 uint32_t _2[248]; Level *pLevelFirst; //+46c uint32_t _3[2]; uint32_t dwStaffTombLevel; // +478 }; struct ActMisc112 { uint32_t _1; //0x00 Act *pAct; //0x04 uint32_t _2[238]; //0x3BC uint32_t dwStaffTombLevel; //0x3C0 uint32_t _3[43]; //0x470 Level *pLevelFirst; }; struct ActMisc113 { uint32_t _1[37]; //0x00 uint32_t dwStaffTombLevel; //0x94 uint32_t _2[245]; //0x98 Act *pAct; //0x46C uint32_t _3[3]; //0x470 Level *pLevelFirst; //0x47C }; struct Act111 { uint32_t _1[2]; ActMisc *pMisc; //+08 }; struct Act112 { uint8_t _1[0x34]; //0x00 Room1 *pRoom1; //0x34 ActMisc *pMisc; //0x38 uint32_t _2[2]; //0x40 uint32_t dwAct; //0x44 }; struct Act113 { uint32_t _1[3]; //0x00 uint32_t dwMapSeed; //0x0C Room1 *pRoom1; //0x10 uint32_t dwAct; //0x14 uint32_t _2[12]; //0x18 ActMisc *pMisc; //0x48 }; #define GETTER_BY_VER(n) \ inline decltype(u113.n) n(D2Version ver) { switch(ver) { case D2_111a: case D2_111b: return u111.n; case D2_112a: return u112.n; default: return u113.n; } } union d2client_struct { d2client_struct_112 u112; d2client_struct_113 u113; }; union RoomTile { RoomTile111 u111; RoomTile112 u112; RoomTile113 u113; GETTER_BY_VER(pNext) GETTER_BY_VER(nNum) GETTER_BY_VER(pRoom2) }; union PresetUnit { PresetUnit111 u111; PresetUnit112 u112; PresetUnit113 u113; GETTER_BY_VER(pPresetNext) GETTER_BY_VER(dwType) GETTER_BY_VER(dwTxtFileNo) GETTER_BY_VER(dwPosX) GETTER_BY_VER(dwPosY) }; union Room1 { Room1_111 u111; Room1_112 u112; Room1_113 u113; GETTER_BY_VER(Coll) }; union Room2 { Room2_111 u111; Room2_112 u112; Room2_113 u113; GETTER_BY_VER(pRoom2Next) GETTER_BY_VER(pRoom1) GETTER_BY_VER(dwPosX) GETTER_BY_VER(dwPosY) GETTER_BY_VER(dwRoomsNear) GETTER_BY_VER(pRoom2Near) GETTER_BY_VER(pLevel) GETTER_BY_VER(dwSizeX) GETTER_BY_VER(dwSizeY) GETTER_BY_VER(pPreset) GETTER_BY_VER(pRoomTiles) }; union Level { Level111 u111; Level112 u112; Level113 u113; GETTER_BY_VER(pRoom2First) GETTER_BY_VER(dwLevelNo) GETTER_BY_VER(dwPosX) GETTER_BY_VER(dwPosY) GETTER_BY_VER(dwSizeX) GETTER_BY_VER(dwSizeY) GETTER_BY_VER(pNextLevel) }; union ActMisc { ActMisc111 u111; ActMisc112 u112; ActMisc113 u113; GETTER_BY_VER(pLevelFirst) }; union Act { Act111 u111; Act112 u112; Act113 u113; GETTER_BY_VER(pMisc) }; #pragma pack(pop) } ================================================ FILE: d2mapapi/genimage.cpp ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #include "d2map.h" #include "session.h" #include "pathfinder.h" #define STBIW_WINDOWS_UTF8 #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb/stb_image_write.h" #include #include #include #include #include int wmain(int argc, wchar_t *argv[]) { if (argc < 5) { fprintf(stderr, "Usage: d2mapapi_gen_image [D2 Game Path] \n"); return -1; } const wchar_t *wfilename = argv[argc > 5 ? 5 : 4]; const wchar_t *ext = StrRChrW(wfilename, nullptr, L'.'); int filetype = -1; if (ext) { if (!StrCmpIW(ext, L".png")) { filetype = 0; } else if (!StrCmpIW(ext, L".bmp")) { filetype = 1; } else if (!StrCmpIW(ext, L".tga")) { filetype = 2; } else if (!StrCmpIW(ext, L".jpg")) { filetype = 3; } } if (filetype < 0) { fprintf(stderr, "Supported file extension: png, bmp, tga, jpg\n"); return -1; } const auto *errstr = argc > 5 ? d2mapapi::d2Init(argv[1]) : nullptr; if (errstr) { do { HKEY key; if (RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Blizzard Entertainment\\Diablo II", 0, KEY_READ, &key) == ERROR_SUCCESS) { wchar_t path[MAX_PATH]; DWORD pathSize = sizeof(path); if (RegQueryValueExW(key, L"InstallPath", nullptr, nullptr, LPBYTE(path), &pathSize) == ERROR_SUCCESS) { errstr = d2mapapi::d2Init(path); if (!errstr) { RegCloseKey(key); break; } } RegCloseKey(key); } fprintf(stderr, "[d2mapapi_gen_image v" D2MAPAPI_VERSION "] %s\n", errstr); return -1; } while (false); } std::uint32_t seed, difficulty, mapId; seed = std::uint32_t(wcstoul(argv[argc > 5 ? 2 : 1], nullptr, 0)); difficulty = std::uint32_t(wcstoul(argv[argc > 5 ? 3 : 2], nullptr, 0)); mapId = std::uint32_t(wcstoul(argv[argc > 5 ? 4 : 3], nullptr, 0)); auto sess = std::make_unique(); sess->update(seed, difficulty); auto *map = sess->getMap(mapId); auto str = map->encode(); auto *collmap = new d2mapapi::CollisionMap(str); auto [x0, y0, x1, y1] = collmap->crop; auto w = collmap->size.width; auto h = collmap->size.height; std::vector vec(w * h); collmap->extractCellData(vec.data(), w, h, x0,y0, 0, 80, 160); for (auto &e: collmap->exits) { for (auto &o: e.second.offsets) { auto ex = o.x - collmap->offset.x, ey = o.y - collmap->offset.y; for (int j = -2; j < 3; ++j) { for (int i = -2; i < 3; ++i) { vec[(ey + j) * w + ex + i] = 255; } } } } stbi_write_png_compression_level = 9; char filename[1024]; stbiw_convert_wchar_to_utf8(filename, 1024, wfilename); switch (filetype) { case 0: stbi_write_png(filename, w, h, 1, vec.data(), w); break; case 1: stbi_write_bmp(filename, w, h, 1, vec.data()); break; case 2: stbi_write_tga(filename, w, h, 1, vec.data()); break; case 3: stbi_write_jpg(filename, w, h, 1, vec.data(), 90); break; default: break; } return 0; } ================================================ FILE: d2mapapi/host.cpp ================================================ #include "pipehost.h" #include int main(int argc, char *argv[]) { d2mapapi::PipedChildProcess pcp; if (!pcp.start(L"d2mapapi_piped.exe", nullptr)) { MessageBoxA(nullptr, pcp.errMsg().c_str(), nullptr, 0); return -1; } for (int i = 1; i < argc; i += 3) { auto *map = pcp.queryMap(strtoul(argv[i], nullptr, 0), strtoul(argv[i + 1], nullptr, 0), strtoul(argv[i + 2], nullptr, 0)); if (!map->built) { MessageBoxA(nullptr, map->errorString.c_str(), nullptr, 0); } delete map; } return 0; } ================================================ FILE: d2mapapi/httpd.cpp ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #include "simphttp.h" #include "d2map.h" #include "session.h" #include #include #include enum { SessionsCacheSize = 8, }; int wmain(int argc, wchar_t *argv[]) { const auto *errstr = argc > 1 ? d2mapapi::d2Init(argv[1]) : "Usage: d2mapapi_piped "; if (errstr) { do { HKEY key; if (RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Blizzard Entertainment\\Diablo II", 0, KEY_READ, &key) == ERROR_SUCCESS) { wchar_t path[MAX_PATH]; DWORD pathSize = sizeof(path); if (RegQueryValueExW(key, L"InstallPath", nullptr, nullptr, LPBYTE(path), &pathSize) == ERROR_SUCCESS) { errstr = d2mapapi::d2Init(path); if (!errstr) { RegCloseKey(key); break; } } RegCloseKey(key); } fprintf(stderr, "[d2mapapi_mod v" D2MAPAPI_VERSION "] %s\n", errstr); return -1; } while (false); } fprintf(stdout, "[d2mapapi_mod v" D2MAPAPI_VERSION "] HTTP server is listening at localhost:8000\n"); std::unordered_map> sessions; std::vector sessionsOrder; simphttp::Server server([&sessions, &sessionsOrder](auto &req, auto &res) { uint32_t seed = 0; uint8_t difficulty = 0; uint32_t levelId = 0; int indentation = 0; bool success = false; const char *url = req.url.c_str(); do { if (*url != '/') break; ++url; char *next; seed = uint32_t(strtoul(url, &next, 0)); if (url == next || *next != '/') break; url = next + 1; difficulty = uint32_t(strtoul(url, &next, 0)); if (url == next || *next != '/') break; url = next + 1; levelId = uint32_t(strtoul(url, &next, 0)); if (url == next || (*next != '/' && *next != 0)) break; if (*next == '/') { url = next + 1; indentation = int(strtoul(url, &next, 0)); if (url == next && (*next == '/' || *next == 0)) { indentation = 0; } else if (*next != '/' && *next != 0) { break; } } success = true; } while (false); res.setStatus(200); res.setHeader("Connection", "keep-alive"); res.setHeader("Content-Type", "application/json"); if (!success) { const std::string errstr = R"({"error":"Invalid parameters!")"; res.setHeader("Content-Length", std::to_string(errstr.size())); res.end(errstr); return; } auto key = uint64_t(seed) | (uint64_t(difficulty) << 32); auto &session = sessions[key]; if (!session) { sessionsOrder.emplace_back(key); session = std::make_unique(); session->update(seed, difficulty); if (sessionsOrder.size() > SessionsCacheSize) { auto oldKey = sessionsOrder[0]; sessionsOrder.erase(sessionsOrder.begin()); sessions.erase(oldKey); } } const auto *map = session->getMap(levelId); if (map) { auto str = map->encode(false, indentation); res.setHeader("Content-Length", std::to_string(str.size())); res.end(str); } else { const std::string errstr = R"({"error":"Invalid map id!"})"; res.setHeader("Content-Length", std::to_string(errstr.size())); res.end(errstr); } }); server.listen("::", 8000); return 0; } ================================================ FILE: d2mapapi/json/LICENSE.MIT ================================================ MIT License Copyright (c) 2013-2021 Niels Lohmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: d2mapapi/json/README.md ================================================ [![JSON for Modern C++](https://raw.githubusercontent.com/nlohmann/json/master/doc/json.gif)](https://github.com/nlohmann/json/releases) [![Build Status](https://app.travis-ci.com/nlohmann/json.svg?branch=develop)](https://app.travis-ci.com/nlohmann/json) [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) [![Ubuntu](https://github.com/nlohmann/json/workflows/Ubuntu/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AUbuntu) [![macOS](https://github.com/nlohmann/json/workflows/macOS/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AmacOS) [![Windows](https://github.com/nlohmann/json/workflows/Windows/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AWindows) [![Coverage Status](https://coveralls.io/repos/github/nlohmann/json/badge.svg?branch=develop)](https://coveralls.io/github/nlohmann/json?branch=develop) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/e0d1a9d5d6fd46fcb655c4cb930bb3e8)](https://www.codacy.com/gh/nlohmann/json/dashboard?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade) [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/nlohmann/json.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nlohmann/json/context:cpp) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/json.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/1mp10JbaANo6FUc7) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](https://nlohmann.github.io/json/doxygen/index.html) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![GitHub Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) [![GitHub Downloads](https://img.shields.io/github/downloads/nlohmann/json/total)](https://github.com/nlohmann/json/releases) [![GitHub Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](https://github.com/nlohmann/json/issues) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/nlohmann/json.svg)](https://isitmaintained.com/project/nlohmann/json "Average time to resolve an issue") [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289) [![GitHub Sponsors](https://img.shields.io/badge/GitHub-Sponsors-ff69b4)](https://github.com/sponsors/nlohmann) - [Design goals](#design-goals) - [Sponsors](#sponsors) - [Support](#support) ([documentation](https://json.nlohmann.me), [FAQ](http://127.0.0.1:8000/home/faq/), [discussions](https://github.com/nlohmann/json/discussions), [API](https://json.nlohmann.me/api/basic_json/), [bug issues](https://github.com/nlohmann/json/issues)) - [Examples](#examples) - [JSON as first-class data type](#json-as-first-class-data-type) - [Serialization / Deserialization](#serialization--deserialization) - [STL-like access](#stl-like-access) - [Conversion from STL containers](#conversion-from-stl-containers) - [JSON Pointer and JSON Patch](#json-pointer-and-json-patch) - [JSON Merge Patch](#json-merge-patch) - [Implicit conversions](#implicit-conversions) - [Conversions to/from arbitrary types](#arbitrary-types-conversions) - [Specializing enum conversion](#specializing-enum-conversion) - [Binary formats (BSON, CBOR, MessagePack, and UBJSON)](#binary-formats-bson-cbor-messagepack-and-ubjson) - [Supported compilers](#supported-compilers) - [Integration](#integration) - [CMake](#cmake) - [Package Managers](#package-managers) - [Pkg-config](#pkg-config) - [License](#license) - [Contact](#contact) - [Thanks](#thanks) - [Used third-party tools](#used-third-party-tools) - [Projects using JSON for Modern C++](#projects-using-json-for-modern-c) - [Notes](#notes) - [Execute unit tests](#execute-unit-tests) ## Design goals There are myriads of [JSON](https://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals: - **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you'll know what I mean. - **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. - **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/test/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). Other aspects were not so important to us: - **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). The default generalization uses the following C++ data types: `std::string` for strings, `int64_t`, `uint64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. However, you can template the generalized class `basic_json` to your needs. - **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set. See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information. ## Sponsors You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nlohmann). ### :label: Named Sponsors - [Michael Hartmann](https://github.com/reFX-Mike) - [Stefan Hagen](https://github.com/sthagen) - [Steve Sperandeo](https://github.com/homer6) - [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe) - [Steve Wagner](https://github.com/ciroque) Thanks everyone! ## Support :question: If you have a **question**, please check if it is already answered in the [**FAQ**](https://json.nlohmann.me/home/faq/) or the [**Q&A**](https://github.com/nlohmann/json/discussions/categories/q-a) section. If not, please [**ask a new question**](https://github.com/nlohmann/json/discussions/new) there. :books: If you want to **learn more** about how to use the library, check out the rest of the [**README**](#examples), have a look at [**code examples**](https://github.com/nlohmann/json/tree/develop/doc/examples), or browse through the [**help pages**](https://json.nlohmann.me). :construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/) or the [**Doxygen documentation**](https://json.nlohmann.me/doxygen/index.html). :bug: If you found a **bug**, please check the [**FAQ**](https://json.nlohmann.me/home/faq/) if it is a known issue or the result of a design decision. Please also have a look at the [**issue list**](https://github.com/nlohmann/json/issues) before you [**create a new issue**](https://github.com/nlohmann/json/issues/new/choose). Please provide as many information as possible to help us understand and reproduce your issue. There is also a [**docset**](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B) for the documentation browsers [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), and [Zeal](https://zealdocs.org) that contains the full [documentation](https://json.nlohmann.me) as offline resource. ## Examples Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/api/basic_json/emplace/)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)). ### JSON as first-class data type Here are some examples to give you an idea how to use the class. Assume you want to create the JSON object ```json { "pi": 3.141, "happy": true, "name": "Niels", "nothing": null, "answer": { "everything": 42 }, "list": [1, 0, 2], "object": { "currency": "USD", "value": 42.99 } } ``` With this library, you could write: ```cpp // create an empty structure (null) json j; // add a number that is stored as double (note the implicit conversion of j to an object) j["pi"] = 3.141; // add a Boolean that is stored as bool j["happy"] = true; // add a string that is stored as std::string j["name"] = "Niels"; // add another null object by passing nullptr j["nothing"] = nullptr; // add an object inside the object j["answer"]["everything"] = 42; // add an array that is stored as std::vector (using an initializer list) j["list"] = { 1, 0, 2 }; // add another object (using an initializer list of pairs) j["object"] = { {"currency", "USD"}, {"value", 42.99} }; // instead, you could also write (which looks very similar to the JSON above) json j2 = { {"pi", 3.141}, {"happy", true}, {"name", "Niels"}, {"nothing", nullptr}, {"answer", { {"everything", 42} }}, {"list", {1, 0, 2}}, {"object", { {"currency", "USD"}, {"value", 42.99} }} }; ``` Note that in all these cases, you never need to "tell" the compiler which JSON value type you want to use. If you want to be explicit or express some edge cases, the functions [`json::array()`](https://nlohmann.github.io/json/api/basic_json/array/) and [`json::object()`](https://nlohmann.github.io/json/api/basic_json/object/) will help: ```cpp // a way to express the empty array [] json empty_array_explicit = json::array(); // ways to express the empty object {} json empty_object_implicit = json({}); json empty_object_explicit = json::object(); // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]] json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} }); ``` ### Serialization / Deserialization #### To/from strings You can create a JSON value (deserialization) by appending `_json` to a string literal: ```cpp // create object from string literal json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; // or even nicer with a raw string literal auto j2 = R"( { "happy": true, "pi": 3.141 } )"_json; ``` Note that without appending the `_json` suffix, the passed string literal is not parsed, but just used as JSON string value. That is, `json j = "{ \"happy\": true, \"pi\": 3.141 }"` would just store the string `"{ "happy": true, "pi": 3.141 }"` rather than parsing the actual object. The above example can also be expressed explicitly using [`json::parse()`](https://nlohmann.github.io/json/api/basic_json/parse/): ```cpp // parse explicitly auto j3 = json::parse(R"({"happy": true, "pi": 3.141})"); ``` You can also get a string representation of a JSON value (serialize): ```cpp // explicit conversion to string std::string s = j.dump(); // {"happy":true,"pi":3.141} // serialization with pretty printing // pass in the amount of spaces to indent std::cout << j.dump(4) << std::endl; // { // "happy": true, // "pi": 3.141 // } ``` Note the difference between serialization and assignment: ```cpp // store a string in a JSON value json j_string = "this is a string"; // retrieve the string value auto cpp_string = j_string.get(); // retrieve the string value (alternative when an variable already exists) std::string cpp_string2; j_string.get_to(cpp_string2); // retrieve the serialized value (explicit JSON serialization) std::string serialized_string = j_string.dump(); // output of original string std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get() << '\n'; // output of serialized value std::cout << j_string << " == " << serialized_string << std::endl; ``` [`.dump()`](https://nlohmann.github.io/json/api/basic_json/dump/) returns the originally stored string value. Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. #### To/from streams (e.g. files, string streams) You can also use streams to serialize and deserialize: ```cpp // deserialize from standard input json j; std::cin >> j; // serialize to standard output std::cout << j; // the setw manipulator was overloaded to set the indentation for pretty printing std::cout << std::setw(4) << j << std::endl; ``` These operators work for any subclasses of `std::istream` or `std::ostream`. Here is the same example with files: ```cpp // read a JSON file std::ifstream i("file.json"); json j; i >> j; // write prettified JSON to another file std::ofstream o("pretty.json"); o << std::setw(4) << j << std::endl; ``` Please note that setting the exception bit for `failbit` is inappropriate for this use case. It will result in program termination due to the `noexcept` specifier in use. #### Read from iterator range You can also parse JSON from an iterator range; that is, from any container accessible by iterators whose `value_type` is an integral type of 1, 2 or 4 bytes, which will be interpreted as UTF-8, UTF-16 and UTF-32 respectively. For instance, a `std::vector`, or a `std::list`: ```cpp std::vector v = {'t', 'r', 'u', 'e'}; json j = json::parse(v.begin(), v.end()); ``` You may leave the iterators for the range [begin, end): ```cpp std::vector v = {'t', 'r', 'u', 'e'}; json j = json::parse(v); ``` #### Custom data source Since the parse function accepts arbitrary iterator ranges, you can provide your own data sources by implementing the `LegacyInputIterator` concept. ```cpp struct MyContainer { void advance(); const char& get_current(); }; struct MyIterator { using difference_type = std::ptrdiff_t; using value_type = char; using pointer = const char*; using reference = const char&; using iterator_category = std::input_iterator_tag; MyIterator& operator++() { MyContainer.advance(); return *this; } bool operator!=(const MyIterator& rhs) const { return rhs.target != target; } reference operator*() const { return target.get_current(); } MyContainer* target = nullptr; }; MyIterator begin(MyContainer& tgt) { return MyIterator{&tgt}; } MyIterator end(const MyContainer&) { return {}; } void foo() { MyContainer c; json j = json::parse(c); } ``` #### SAX interface The library uses a SAX-like interface with the following functions: ```cpp // called when null is parsed bool null(); // called when a boolean is parsed; value is passed bool boolean(bool val); // called when a signed or unsigned integer number is parsed; value is passed bool number_integer(number_integer_t val); bool number_unsigned(number_unsigned_t val); // called when a floating-point number is parsed; value and original string is passed bool number_float(number_float_t val, const string_t& s); // called when a string is parsed; value is passed and can be safely moved away bool string(string_t& val); // called when a binary value is parsed; value is passed and can be safely moved away bool binary(binary_t& val); // called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known) bool start_object(std::size_t elements); bool end_object(); bool start_array(std::size_t elements); bool end_array(); // called when an object key is parsed; value is passed and can be safely moved away bool key(string_t& val); // called when a parse error occurs; byte position, the last token, and an exception is passed bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex); ``` The return value of each function determines whether parsing should proceed. To implement your own SAX handler, proceed as follows: 1. Implement the SAX interface in a class. You can use class `nlohmann::json_sax` as base class, but you can also use any class where the functions described above are implemented and public. 2. Create an object of your SAX interface class, e.g. `my_sax`. 3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface. Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp). ### STL-like access We designed the JSON class to behave just like an STL container. In fact, it satisfies the [**ReversibleContainer**](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) requirement. ```cpp // create an array using push_back json j; j.push_back("foo"); j.push_back(1); j.push_back(true); // also use emplace_back j.emplace_back(1.78); // iterate the array for (json::iterator it = j.begin(); it != j.end(); ++it) { std::cout << *it << '\n'; } // range-based for for (auto& element : j) { std::cout << element << '\n'; } // getter/setter const auto tmp = j[0].get(); j[1] = 42; bool foo = j.at(2); // comparison j == R"(["foo", 1, true, 1.78])"_json; // true // other stuff j.size(); // 4 entries j.empty(); // false j.type(); // json::value_t::array j.clear(); // the array is empty again // convenience type checkers j.is_null(); j.is_boolean(); j.is_number(); j.is_object(); j.is_array(); j.is_string(); // create an object json o; o["foo"] = 23; o["bar"] = false; o["baz"] = 3.141; // also use emplace o.emplace("weather", "sunny"); // special iterator member functions for objects for (json::iterator it = o.begin(); it != o.end(); ++it) { std::cout << it.key() << " : " << it.value() << "\n"; } // the same code as range for for (auto& el : o.items()) { std::cout << el.key() << " : " << el.value() << "\n"; } // even easier with structured bindings (C++17) for (auto& [key, value] : o.items()) { std::cout << key << " : " << value << "\n"; } // find an entry if (o.contains("foo")) { // there is an entry with key "foo" } // or via find and an iterator if (o.find("foo") != o.end()) { // there is an entry with key "foo" } // or simpler using count() int foo_present = o.count("foo"); // 1 int fob_present = o.count("fob"); // 0 // delete an entry o.erase("foo"); ``` ### Conversion from STL containers Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON values (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends on how the elements are ordered in the respective STL container. ```cpp std::vector c_vector {1, 2, 3, 4}; json j_vec(c_vector); // [1, 2, 3, 4] std::deque c_deque {1.2, 2.3, 3.4, 5.6}; json j_deque(c_deque); // [1.2, 2.3, 3.4, 5.6] std::list c_list {true, true, false, true}; json j_list(c_list); // [true, true, false, true] std::forward_list c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543}; json j_flist(c_flist); // [12345678909876, 23456789098765, 34567890987654, 45678909876543] std::array c_array {{1, 2, 3, 4}}; json j_array(c_array); // [1, 2, 3, 4] std::set c_set {"one", "two", "three", "four", "one"}; json j_set(c_set); // only one entry for "one" is used // ["four", "one", "three", "two"] std::unordered_set c_uset {"one", "two", "three", "four", "one"}; json j_uset(c_uset); // only one entry for "one" is used // maybe ["two", "three", "four", "one"] std::multiset c_mset {"one", "two", "one", "four"}; json j_mset(c_mset); // both entries for "one" are used // maybe ["one", "two", "one", "four"] std::unordered_multiset c_umset {"one", "two", "one", "four"}; json j_umset(c_umset); // both entries for "one" are used // maybe ["one", "two", "one", "four"] ``` Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON values (see examples above) can be used to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container. ```cpp std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; json j_map(c_map); // {"one": 1, "three": 3, "two": 2 } std::unordered_map c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} }; json j_umap(c_umap); // {"one": 1.2, "two": 2.3, "three": 3.4} std::multimap c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; json j_mmap(c_mmap); // only one entry for key "three" is used // maybe {"one": true, "two": true, "three": true} std::unordered_multimap c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; json j_ummap(c_ummap); // only one entry for key "three" is used // maybe {"one": true, "two": true, "three": true} ``` ### JSON Pointer and JSON Patch The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as alternative means to address structured values. On top of this, **JSON Patch** ([RFC 6902](https://tools.ietf.org/html/rfc6902)) allows to describe differences between two JSON values - effectively allowing patch and diff operations known from Unix. ```cpp // a JSON value json j_original = R"({ "baz": ["one", "two", "three"], "foo": "bar" })"_json; // access members with a JSON pointer (RFC 6901) j_original["/baz/1"_json_pointer]; // "two" // a JSON patch (RFC 6902) json j_patch = R"([ { "op": "replace", "path": "/baz", "value": "boo" }, { "op": "add", "path": "/hello", "value": ["world"] }, { "op": "remove", "path": "/foo"} ])"_json; // apply the patch json j_result = j_original.patch(j_patch); // { // "baz": "boo", // "hello": ["world"] // } // calculate a JSON patch from two JSON values json::diff(j_result, j_original); // [ // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, // { "op": "remove","path": "/hello" }, // { "op": "add", "path": "/foo", "value": "bar" } // ] ``` ### JSON Merge Patch The library supports **JSON Merge Patch** ([RFC 7386](https://tools.ietf.org/html/rfc7386)) as a patch format. Instead of using JSON Pointer (see above) to specify values to be manipulated, it describes the changes using a syntax that closely mimics the document being modified. ```cpp // a JSON value json j_document = R"({ "a": "b", "c": { "d": "e", "f": "g" } })"_json; // a patch json j_patch = R"({ "a":"z", "c": { "f": null } })"_json; // apply the patch j_document.merge_patch(j_patch); // { // "a": "z", // "c": { // "d": "e" // } // } ``` ### Implicit conversions Supported types can be implicitly converted to JSON values. It is recommended to **NOT USE** implicit conversions **FROM** a JSON value. You can find more details about this recommendation [here](https://www.github.com/nlohmann/json/issues/958). You can switch off implicit conversions by defining `JSON_USE_IMPLICIT_CONVERSIONS` to `0` before including the `json.hpp` header. When using CMake, you can also achieve this by setting the option `JSON_ImplicitConversions` to `OFF`. ```cpp // strings std::string s1 = "Hello, world!"; json js = s1; auto s2 = js.get(); // NOT RECOMMENDED std::string s3 = js; std::string s4; s4 = js; // Booleans bool b1 = true; json jb = b1; auto b2 = jb.get(); // NOT RECOMMENDED bool b3 = jb; bool b4; b4 = jb; // numbers int i = 42; json jn = i; auto f = jn.get(); // NOT RECOMMENDED double f2 = jb; double f3; f3 = jb; // etc. ``` Note that `char` types are not automatically converted to JSON strings, but to integer numbers. A conversion to a string must be specified explicitly: ```cpp char ch = 'A'; // ASCII value 65 json j_default = ch; // stores integer number 65 json j_string = std::string(1, ch); // stores string "A" ``` ### Arbitrary types conversions Every type can be serialized in JSON, not just STL containers and scalar types. Usually, you would do something along those lines: ```cpp namespace ns { // a simple struct to model a person struct person { std::string name; std::string address; int age; }; } ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; // convert to JSON: copy each value into the JSON object json j; j["name"] = p.name; j["address"] = p.address; j["age"] = p.age; // ... // convert from JSON: copy each value from the JSON object ns::person p { j["name"].get(), j["address"].get(), j["age"].get() }; ``` It works, but that's quite a lot of boilerplate... Fortunately, there's a better way: ```cpp // create a person ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60}; // conversion: person -> json json j = p; std::cout << j << std::endl; // {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} // conversion: json -> person auto p2 = j.get(); // that's it assert(p == p2); ``` #### Basic usage To make this work with one of your types, you only need to provide two functions: ```cpp using json = nlohmann::json; namespace ns { void to_json(json& j, const person& p) { j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}}; } void from_json(const json& j, person& p) { j.at("name").get_to(p.name); j.at("address").get_to(p.address); j.at("age").get_to(p.age); } } // namespace ns ``` That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called. Likewise, when calling `get()` or `get_to(your_type&)`, the `from_json` method will be called. Some important things: * Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined). * Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. * When using `get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) * In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/api/basic_json/at/) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. * You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. #### Simplify your life with macros If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. There are two macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object: - `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the namespace of the class/struct to create code for. - `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the class/struct to create code for. This macro can also access private members. In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. ##### Examples The `to_json`/`from_json` functions for the `person` struct above can be created with: ```cpp namespace ns { NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age) } ``` Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed: ```cpp namespace ns { class address { private: std::string street; int housenumber; int postcode; public: NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode) }; } ``` #### How do I convert third-party types? This requires a bit more advanced technique. But first, let's see how this conversion mechanism works: The library uses **JSON Serializers** to convert types to json. The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](https://en.cppreference.com/w/cpp/language/adl)). It is implemented like this (simplified): ```cpp template struct adl_serializer { static void to_json(json& j, const T& value) { // calls the "to_json" method in T's namespace } static void from_json(const json& j, T& value) { // same thing, but with the "from_json" method } }; ``` This serializer works fine when you have control over the type's namespace. However, what about `boost::optional` or `std::filesystem::path` (C++17)? Hijacking the `boost` namespace is pretty bad, and it's illegal to add something other than template specializations to `std`... To solve this, you need to add a specialization of `adl_serializer` to the `nlohmann` namespace, here's an example: ```cpp // partial specialization (full specialization works too) namespace nlohmann { template struct adl_serializer> { static void to_json(json& j, const boost::optional& opt) { if (opt == boost::none) { j = nullptr; } else { j = *opt; // this will call adl_serializer::to_json which will // find the free function to_json in T's namespace! } } static void from_json(const json& j, boost::optional& opt) { if (j.is_null()) { opt = boost::none; } else { opt = j.get(); // same as above, but with // adl_serializer::from_json } } }; } ``` #### How can I use `get()` for non-default constructible/non-copyable types? There is a way, if your type is [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload: ```cpp struct move_only_type { move_only_type() = delete; move_only_type(int ii): i(ii) {} move_only_type(const move_only_type&) = delete; move_only_type(move_only_type&&) = default; int i; }; namespace nlohmann { template <> struct adl_serializer { // note: the return type is no longer 'void', and the method only takes // one argument static move_only_type from_json(const json& j) { return {j.get()}; } // Here's the catch! You must provide a to_json method! Otherwise you // will not be able to convert move_only_type to json, since you fully // specialized adl_serializer on that type static void to_json(json& j, move_only_type t) { j = t.i; } }; } ``` #### Can I write my own serializer? (Advanced use) Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/test/src/unit-udt.cpp) in the test suite, to see a few examples. If you write your own serializer, you'll need to do a few things: - use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`) - use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods - use `nlohmann::to_json` and `nlohmann::from_json` when you need ADL Here is an example, without simplifications, that only accepts types with a size <= 32, and uses ADL. ```cpp // You should use void as a second template argument // if you don't need compile-time checks on T template::type> struct less_than_32_serializer { template static void to_json(BasicJsonType& j, T value) { // we want to use ADL, and call the correct to_json overload using nlohmann::to_json; // this method is called by adl_serializer, // this is where the magic happens to_json(j, value); } template static void from_json(const BasicJsonType& j, T& value) { // same thing here using nlohmann::from_json; from_json(j, value); } }; ``` Be **very** careful when reimplementing your serializer, you can stack overflow if you don't pay attention: ```cpp template struct bad_serializer { template static void to_json(BasicJsonType& j, const T& value) { // this calls BasicJsonType::json_serializer::to_json(j, value); // if BasicJsonType::json_serializer == bad_serializer ... oops! j = value; } template static void to_json(const BasicJsonType& j, T& value) { // this calls BasicJsonType::json_serializer::from_json(j, value); // if BasicJsonType::json_serializer == bad_serializer ... oops! value = j.template get(); // oops! } }; ``` ### Specializing enum conversion By default, enum values are serialized to JSON as integers. In some cases this could result in undesired behavior. If an enum is modified or re-ordered after data has been serialized to JSON, the later de-serialized JSON data may be undefined or a different enum value than was originally intended. It is possible to more precisely specify how a given enum is mapped to and from JSON as shown below: ```cpp // example enum type declaration enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED, TS_INVALID=-1, }; // map TaskState values to JSON as strings NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { {TS_INVALID, nullptr}, {TS_STOPPED, "stopped"}, {TS_RUNNING, "running"}, {TS_COMPLETED, "completed"}, }) ``` The `NLOHMANN_JSON_SERIALIZE_ENUM()` macro declares a set of `to_json()` / `from_json()` functions for type `TaskState` while avoiding repetition and boilerplate serialization code. **Usage:** ```cpp // enum to JSON as string json j = TS_STOPPED; assert(j == "stopped"); // json string to enum json j3 = "running"; assert(j3.get() == TS_RUNNING); // undefined json value to enum (where the first map entry above is the default) json jPi = 3.14; assert(jPi.get() == TS_INVALID ); ``` Just as in [Arbitrary Type Conversions](#arbitrary-types-conversions) above, - `NLOHMANN_JSON_SERIALIZE_ENUM()` MUST be declared in your enum type's namespace (which can be the global namespace), or the library will not be able to locate it and it will default to integer serialization. - It MUST be available (e.g., proper headers must be included) everywhere you use the conversions. Other Important points: - When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. - If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON. ### Binary formats (BSON, CBOR, MessagePack, and UBJSON) Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [BSON](https://bsonspec.org) (Binary JSON), [CBOR](https://cbor.io) (Concise Binary Object Representation), [MessagePack](https://msgpack.org), and [UBJSON](https://ubjson.org) (Universal Binary JSON Specification) to efficiently encode JSON values to byte vectors and to decode such vectors. ```cpp // create a JSON value json j = R"({"compact": true, "schema": 0})"_json; // serialize to BSON std::vector v_bson = json::to_bson(j); // 0x1B, 0x00, 0x00, 0x00, 0x08, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x00, 0x01, 0x10, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // roundtrip json j_from_bson = json::from_bson(v_bson); // serialize to CBOR std::vector v_cbor = json::to_cbor(j); // 0xA2, 0x67, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xF5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00 // roundtrip json j_from_cbor = json::from_cbor(v_cbor); // serialize to MessagePack std::vector v_msgpack = json::to_msgpack(j); // 0x82, 0xA7, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xC3, 0xA6, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00 // roundtrip json j_from_msgpack = json::from_msgpack(v_msgpack); // serialize to UBJSON std::vector v_ubjson = json::to_ubjson(j); // 0x7B, 0x69, 0x07, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x54, 0x69, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x69, 0x00, 0x7D // roundtrip json j_from_ubjson = json::from_ubjson(v_ubjson); ``` The library also supports binary types from BSON, CBOR (byte strings), and MessagePack (bin, ext, fixext). They are stored by default as `std::vector` to be processed outside of the library. ```cpp // CBOR byte string with payload 0xCAFE std::vector v = {0x42, 0xCA, 0xFE}; // read value json j = json::from_cbor(v); // the JSON value has type binary j.is_binary(); // true // get reference to stored binary value auto& binary = j.get_binary(); // the binary value has no subtype (CBOR has no binary subtypes) binary.has_subtype(); // false // access std::vector member functions binary.size(); // 2 binary[0]; // 0xCA binary[1]; // 0xFE // set subtype to 0x10 binary.set_subtype(0x10); // serialize to MessagePack auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE ``` ## Supported compilers Though it's 2021 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work: - GCC 4.8 - 11.0 (and possibly later) - Clang 3.4 - 13.0 (and possibly later) - Apple Clang 9.1 - 12.4 (and possibly later) - Intel C++ Compiler 17.0.2 (and possibly later) - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) - Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later) - Microsoft Visual C++ 2019 / Build Tools 16.3.1+1def00d3d (and possibly later) I would be happy to learn about other compilers/versions. Please note: - GCC 4.8 has a bug [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824)): multiline raw strings cannot be the arguments to macros. Don't use multiline raw strings directly in macros with this compiler. - Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your `Application.mk`. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default. ``` APP_STL := c++_shared NDK_TOOLCHAIN_VERSION := clang3.6 APP_CPPFLAGS += -frtti -fexceptions ``` The code compiles successfully with [Android NDK](https://developer.android.com/ndk/index.html?hl=ml), Revision 9 - 11 (and possibly later) and [CrystaX's Android NDK](https://www.crystax.net/en/android/ndk) version 10. - For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`) may occur. Note this is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to [this site](https://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219). - Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case. The following compilers are currently used in continuous integration at [Travis](https://travis-ci.org/nlohmann/json), [AppVeyor](https://ci.appveyor.com/project/nlohmann/json), [Drone CI](https://cloud.drone.io/nlohmann/json), and [GitHub Actions](https://github.com/nlohmann/json/actions): | Compiler | Operating System | CI Provider | |-------------------------------------------------------------------|--------------------|----------------| | Apple Clang 10.0.1 (clang-1001.0.46.4); Xcode 10.2.1 | macOS 10.14.4 | Travis | | Apple Clang 10.0.1 (clang-1001.0.46.4); Xcode 10.3 | macOS 10.15.7 | GitHub Actions | | Apple Clang 11.0.0 (clang-1100.0.33.12); Xcode 11.2.1 | macOS 10.15.7 | GitHub Actions | | Apple Clang 11.0.0 (clang-1100.0.33.17); Xcode 11.3.1 | macOS 10.15.7 | GitHub Actions | | Apple Clang 11.0.3 (clang-1103.0.32.59); Xcode 11.4.1 | macOS 10.15.7 | GitHub Actions | | Apple Clang 11.0.3 (clang-1103.0.32.62); Xcode 11.5 | macOS 10.15.7 | GitHub Actions | | Apple Clang 11.0.3 (clang-1103.0.32.62); Xcode 11.6 | macOS 10.15.7 | GitHub Actions | | Apple Clang 11.0.3 (clang-1103.0.32.62); Xcode 11.7 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.2); Xcode 12 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.21); Xcode 12.1 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.21); Xcode 12.1.1 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.27); Xcode 12.2 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.28); Xcode 12.3 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.29); Xcode 12.4 | macOS 10.15.7 | GitHub Actions | | GCC 4.8.5 (Ubuntu 4.8.5-4ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 4.9.3 (Ubuntu 4.9.3-13ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 6.5.0 (Ubuntu 6.5.0-2ubuntu1~14.04.1) | Ubuntu 14.04.5 LTS | Travis | | GCC 7.5.0 (Ubuntu 7.5.0-6ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project) | Windows-10.0.17763 | GitHub Actions | | GCC 8.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project) | Windows-10.0.17763 | GitHub Actions | | GCC 8.4.0 (Ubuntu 8.4.0-3ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 11.0.1 20210321 (experimental) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 11.1.0 | Ubuntu (aarch64) | Drone CI | | Clang 3.5.2 (3.5.2-3ubuntu1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.6.2 (3.6.2-3ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.7.1 (3.7.1-2ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.8.0 (3.8.0-2ubuntu4) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.9.1 (3.9.1-4ubuntu3\~16.04.2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 4.0.0 (4.0.0-1ubuntu1\~16.04.2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 5.0.0 (5.0.0-3\~16.04.1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 6.0.1 (6.0.1-14) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 7.0.1 (7.0.1-12) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 8.0.1 (8.0.1-9) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 9.0.1 (9.0.1-12) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 10.0.0 (10.0.0-4ubuntu1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 10.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | | Clang 11.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | | Clang 11.0.0 with MSVC-like command-line | Windows-10.0.17763 | GitHub Actions | | Clang 11.0.0 (11.0.0-2~ubuntu20.04.1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 12.0.0 (12.0.0-3ubuntu1~20.04.3) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 13.0.0 (13.0.0-++20210828094952+9c49fee5e7ac-1exp120210828075752.71 | Ubuntu 20.04.2 LTS | GitHub Actions | | Visual Studio 14 2015 MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1) | Windows-6.3.9600 | AppVeyor | | Visual Studio 15 2017 MSVC 19.16.27035.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor | | Visual Studio 15 2017 MSVC 19.16.27045.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | GitHub Actions | | Visual Studio 16 2019 MSVC 19.28.29912.0 (Build Engine version 16.9.0+57a23d249 for .NET Framework) | Windows-10.0.17763 | GitHub Actions | | Visual Studio 16 2019 MSVC 19.28.29912.0 (Build Engine version 16.9.0+57a23d249 for .NET Framework) | Windows-10.0.17763 | AppVeyor | ## Integration [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) is the single required file in `single_include/nlohmann` or [released here](https://github.com/nlohmann/json/releases). You need to add ```cpp #include // for convenience using json = nlohmann::json; ``` to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang). You can further use file [`include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_fwd.hpp) for forward-declarations. The installation of json_fwd.hpp (as part of cmake's install step), can be achieved by setting `-DJSON_MultipleHeaders=ON`. ### CMake You can also use the `nlohmann_json::nlohmann_json` interface target in CMake. This target populates the appropriate usage requirements for `INTERFACE_INCLUDE_DIRECTORIES` to point to the appropriate include directories and `INTERFACE_COMPILE_FEATURES` for the necessary C++11 flags. #### External To use this library from a CMake project, you can locate it directly with `find_package()` and use the namespaced imported target from the generated package configuration: ```cmake # CMakeLists.txt find_package(nlohmann_json 3.2.0 REQUIRED) ... add_library(foo ...) ... target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) ``` The package configuration file, `nlohmann_jsonConfig.cmake`, can be used either from an install tree or directly out of the build tree. #### Embedded To embed the library directly into an existing CMake project, place the entire source tree in a subdirectory and call `add_subdirectory()` in your `CMakeLists.txt` file: ```cmake # Typically you don't care so much for a third party library's tests to be # run from your own project's code. set(JSON_BuildTests OFF CACHE INTERNAL "") # If you only include this third party in PRIVATE source files, you do not # need to install it when your main project gets installed. # set(JSON_Install OFF CACHE INTERNAL "") # Don't use include(nlohmann_json/CMakeLists.txt) since that carries with it # unintended consequences that will break the build. It's generally # discouraged (although not necessarily well documented as such) to use # include(...) for pulling in other CMake projects anyways. add_subdirectory(nlohmann_json) ... add_library(foo ...) ... target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) ``` ##### Embedded (FetchContent) Since CMake v3.11, [FetchContent](https://cmake.org/cmake/help/v3.11/module/FetchContent.html) can be used to automatically download the repository as a dependency at configure time. Example: ```cmake include(FetchContent) FetchContent_Declare(json GIT_REPOSITORY https://github.com/nlohmann/json.git GIT_TAG v3.7.3) FetchContent_GetProperties(json) if(NOT json_POPULATED) FetchContent_Populate(json) add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) endif() target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) ``` **Note**: The repository https://github.com/nlohmann/json download size is huge. It contains all the dataset used for the benchmarks. You might want to depend on a smaller repository. For instance, you might want to replace the URL above by https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent #### Supporting Both To allow your project to support either an externally supplied or an embedded JSON library, you can use a pattern akin to the following: ``` cmake # Top level CMakeLists.txt project(FOO) ... option(FOO_USE_EXTERNAL_JSON "Use an external JSON library" OFF) ... add_subdirectory(thirdparty) ... add_library(foo ...) ... # Note that the namespaced target will always be available regardless of the # import method target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) ``` ```cmake # thirdparty/CMakeLists.txt ... if(FOO_USE_EXTERNAL_JSON) find_package(nlohmann_json 3.2.0 REQUIRED) else() set(JSON_BuildTests OFF CACHE INTERNAL "") add_subdirectory(nlohmann_json) endif() ... ``` `thirdparty/nlohmann_json` is then a complete copy of this source tree. ### Package Managers :beer: If you are using OS X and [Homebrew](https://brew.sh), just type `brew install nlohmann-json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann-json --HEAD`. See [nlohmann-json](https://formulae.brew.sh/formula/nlohmann-json) for more information. If you are using the [Meson Build System](https://mesonbuild.com), add this source tree as a [meson subproject](https://mesonbuild.com/Subprojects.html#using-a-subproject). You may also use the `include.zip` published in this project's [Releases](https://github.com/nlohmann/json/releases) to reduce the size of the vendored source tree. Alternatively, you can get a wrap file by downloading it from [Meson WrapDB](https://wrapdb.mesonbuild.com/nlohmann_json), or simply use `meson wrap install nlohmann_json`. Please see the meson project for any issues regarding the packaging. The provided meson.build can also be used as an alternative to cmake for installing `nlohmann_json` system-wide in which case a pkg-config file is installed. To use it, simply have your build system require the `nlohmann_json` pkg-config dependency. In Meson, it is preferred to use the [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than using the subproject directly. If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add [`nlohmann_json/x.y.z`](https://conan.io/center/nlohmann_json) to your `conanfile`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/conan-io/conan-center-index/issues) if you experience problems with the packages. If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the [`nlohmann-json` package](https://spack.readthedocs.io/en/latest/package_list.html#nlohmann-json). Please see the [spack project](https://github.com/spack/spack) for any issues regarding the packaging. If you are using [hunter](https://github.com/cpp-pm/hunter) on your project for external dependencies, then you can use the [nlohmann_json package](https://hunter.readthedocs.io/en/latest/packages/pkg/nlohmann_json.html). Please see the hunter project for any issues regarding the packaging. If you are using [Buckaroo](https://buckaroo.pm), you can install this library's module with `buckaroo add github.com/buckaroo-pm/nlohmann-json`. Please file issues [here](https://github.com/buckaroo-pm/nlohmann-json). There is a demo repo [here](https://github.com/njlr/buckaroo-nholmann-json-example). If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can install the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json) with `vcpkg install nlohmann-json` and follow the then displayed descriptions. Please see the vcpkg project for any issues regarding the packaging. If you are using [cget](https://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install nlohmann/json`. A specific version can be installed with `cget install nlohmann/json@v3.1.0`. Also, the multiple header version can be installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nlohmann/json -DJSON_MultipleHeaders=ON`). If you are using [CocoaPods](https://cocoapods.org), you can use the library by adding pod `"nlohmann_json", '~>3.1.2'` to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues [here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open). If you are using [NuGet](https://www.nuget.org), you can use the package [nlohmann.json](https://www.nuget.org/packages/nlohmann.json/). Please check [this extensive description](https://github.com/nlohmann/json/issues/1132#issuecomment-452250255) on how to use the package. Please files issues [here](https://github.com/hnkb/nlohmann-json-nuget/issues). If you are using [conda](https://conda.io/), you can use the package [nlohmann_json](https://github.com/conda-forge/nlohmann_json-feedstock) from [conda-forge](https://conda-forge.org) executing `conda install -c conda-forge nlohmann_json`. Please file issues [here](https://github.com/conda-forge/nlohmann_json-feedstock/issues). If you are using [MSYS2](https://www.msys2.org/), you can use the [mingw-w64-nlohmann-json](https://packages.msys2.org/base/mingw-w64-nlohmann-json) package, just type `pacman -S mingw-w64-i686-nlohmann-json` or `pacman -S mingw-w64-x86_64-nlohmann-json` for installation. Please file issues [here](https://github.com/msys2/MINGW-packages/issues/new?title=%5Bnlohmann-json%5D) if you experience problems with the packages. If you are using [MacPorts](https://ports.macports.org), execute `sudo port install nlohmann-json` to install the [nlohmann-json](https://ports.macports.org/port/nlohmann-json/) package. If you are using [`build2`](https://build2.org), you can use the [`nlohmann-json`](https://cppget.org/nlohmann-json) package from the public repository https://cppget.org or directly from the [package's sources repository](https://github.com/build2-packaging/nlohmann-json). In your project's `manifest` file, just add `depends: nlohmann-json` (probably with some [version constraints](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-add-remove-deps)). If you are not familiar with using dependencies in `build2`, [please read this introduction](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml). Please file issues [here](https://github.com/build2-packaging/nlohmann-json) if you experience problems with the packages. If you are using [`wsjcpp`](https://wsjcpp.org), you can use the command `wsjcpp install "https://github.com/nlohmann/json:develop"` to get the latest version. Note you can change the branch ":develop" to an existing tag or another branch. If you are using [`CPM.cmake`](https://github.com/TheLartians/CPM.cmake), you can check this [`example`](https://github.com/TheLartians/CPM.cmake/tree/master/examples/json). After [adding CPM script](https://github.com/TheLartians/CPM.cmake#adding-cpm) to your project, implement the following snippet to your CMake: ```cmake CPMAddPackage( NAME nlohmann_json GITHUB_REPOSITORY nlohmann/json VERSION 3.9.1) ``` ### Pkg-config If you are using bare Makefiles, you can use `pkg-config` to generate the include flags that point to where the library is installed: ```sh pkg-config nlohmann_json --cflags ``` Users of the Meson build system will also be able to use a system wide library, which will be found by `pkg-config`: ```meson json = dependency('nlohmann_json', required: true) ``` ## License The class is licensed under the [MIT License](https://opensource.org/licenses/MIT): Copyright © 2013-2021 [Niels Lohmann](https://nlohmann.me) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * * The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](https://bjoern.hoehrmann.de/) The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/) The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0). ## Contact If you have questions regarding the library, I would like to invite you to [open an issue at GitHub](https://github.com/nlohmann/json/issues/new/choose). Please describe your request, problem, or question as detailed as possible, and also mention the version of the library you are using as well as the version of your compiler and operating system. Opening an issue at GitHub allows other users and contributors to this library to collaborate. For instance, I have little experience with MSVC, and most issues in this regard have been solved by a growing community. If you have a look at the [closed issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+is%3Aclosed), you will see that we react quite timely in most cases. Only if your request would contain confidential information, please [send me an email](mailto:mail@nlohmann.me). For encrypted messages, please use [this key](https://keybase.io/nlohmann/pgp_keys.asc). ## Security [Commits by Niels Lohmann](https://github.com/nlohmann/json/commits) and [releases](https://github.com/nlohmann/json/releases) are signed with this [PGP Key](https://keybase.io/nlohmann/pgp_keys.asc?fingerprint=797167ae41c0a6d9232e48457f3cea63ae251b69). ## Thanks I deeply appreciate the help of the following people. - [Teemperor](https://github.com/Teemperor) implemented CMake support and lcov integration, realized escape and Unicode handling in the string parser, and fixed the JSON serialization. - [elliotgoodrich](https://github.com/elliotgoodrich) fixed an issue with double deletion in the iterator classes. - [kirkshoop](https://github.com/kirkshoop) made the iterators of the class composable to other libraries. - [wancw](https://github.com/wanwc) fixed a bug that hindered the class to compile with Clang. - Tomas Åblad found a bug in the iterator implementation. - [Joshua C. Randall](https://github.com/jrandall) fixed a bug in the floating-point serialization. - [Aaron Burghardt](https://github.com/aburgh) implemented code to parse streams incrementally. Furthermore, he greatly improved the parser class by allowing the definition of a filter function to discard undesired elements while parsing. - [Daniel Kopeček](https://github.com/dkopecek) fixed a bug in the compilation with GCC 5.0. - [Florian Weber](https://github.com/Florianjw) fixed a bug in and improved the performance of the comparison operators. - [Eric Cornelius](https://github.com/EricMCornelius) pointed out a bug in the handling with NaN and infinity values. He also improved the performance of the string escaping. - [易思龙](https://github.com/likebeta) implemented a conversion from anonymous enums. - [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual studio. - [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types. - [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling. - [dariomt](https://github.com/dariomt) fixed some typos in the examples. - [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation. - [Colin Hirsch](https://github.com/ColinH) took care of a small namespace issue. - [Huu Nguyen](https://github.com/whoshuu) correct a variable name in the documentation. - [Silverweed](https://github.com/silverweed) overloaded `parse()` to accept an rvalue reference. - [dariomt](https://github.com/dariomt) fixed a subtlety in MSVC type support and implemented the `get_ref()` function to get a reference to stored values. - [ZahlGraf](https://github.com/ZahlGraf) added a workaround that allows compilation using Android NDK. - [whackashoe](https://github.com/whackashoe) replaced a function that was marked as unsafe by Visual Studio. - [406345](https://github.com/406345) fixed two small warnings. - [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function. - [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines. - [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers and implemented better roundtrip support for parsed numbers. - [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file. - [msm-](https://github.com/msm-) added support for American Fuzzy Lop. - [Annihil](https://github.com/Annihil) fixed an example in the README file. - [Themercee](https://github.com/Themercee) noted a wrong URL in the README file. - [Lv Zheng](https://github.com/lv-zheng) fixed a namespace issue with `int64_t` and `uint64_t`. - [abc100m](https://github.com/abc100m) analyzed the issues with GCC 4.8 and proposed a [partial solution](https://github.com/nlohmann/json/pull/212). - [zewt](https://github.com/zewt) added useful notes to the README file about Android. - [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake. - [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files. - [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal). - [Mário Feroldi](https://github.com/thelostt) fixed a small typo. - [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release. - [Damien](https://github.com/dtoma) fixed one of the last conversion warnings. - [Thomas Braun](https://github.com/t-b) fixed a warning in a test case and adjusted MSVC calls in the CI. - [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types and split the single header file into smaller chunks. - [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation. - [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`. - [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion. - [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix. - [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file. - [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function. - [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](https://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing. - [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan. - [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning. - [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. - [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one. - [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers. - [Jonathan Lee](https://github.com/vjon) fixed an example in the README file. - [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types. - [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio. - [Jared Grubb](https://github.com/jaredgrubb) supported the implementation of user-defined types. - [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example. - [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite. - [ukhegg](https://github.com/ukhegg) found proposed an improvement for the examples section. - [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README. - [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s. - [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation. - [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README, removed unnecessary bit arithmetic, and fixed some `-Weffc++` warnings. - [Krzysztof Woś](https://github.com/krzysztofwos) made exceptions more visible. - [ftillier](https://github.com/ftillier) fixed a compiler warning. - [tinloaf](https://github.com/tinloaf) made sure all pushed warnings are properly popped. - [Fytch](https://github.com/Fytch) found a bug in the documentation. - [Jay Sistar](https://github.com/Type1J) implemented a Meson build description. - [Henry Lee](https://github.com/HenryRLee) fixed a warning in ICC and improved the iterator implementation. - [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager. - [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`. - [Mike Tzou](https://github.com/Chocobo1) fixed some typos. - [amrcode](https://github.com/amrcode) noted a misleading documentation about comparison of floats. - [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `` with ``. - [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library. - [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists. - [Greg Hurrell](https://github.com/wincent) fixed a typo. - [Dmitry Kukovinets](https://github.com/DmitryKuk) fixed a typo. - [kbthomp1](https://github.com/kbthomp1) fixed an issue related to the Intel OSX compiler. - [Markus Werle](https://github.com/daixtrose) fixed a typo. - [WebProdPP](https://github.com/WebProdPP) fixed a subtle error in a precondition check. - [Alex](https://github.com/leha-bot) noted an error in a code sample. - [Tom de Geus](https://github.com/tdegeus) reported some warnings with ICC and helped fixing them. - [Perry Kundert](https://github.com/pjkundert) simplified reading from input streams. - [Sonu Lohani](https://github.com/sonulohani) fixed a small compilation error. - [Jamie Seward](https://github.com/jseward) fixed all MSVC warnings. - [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file. - [pvleuven](https://github.com/pvleuven) helped fixing a warning in ICC. - [Pavel](https://github.com/crea7or) helped fixing some warnings in MSVC. - [Jamie Seward](https://github.com/jseward) avoided unnecessary string copies in `find()` and `count()`. - [Mitja](https://github.com/Itja) fixed some typos. - [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links. - [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view. - [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings. - [Eren Okka](https://github.com/erengy) fixed some MSVC warnings. - [abolz](https://github.com/abolz) integrated the Grisu2 algorithm for proper floating-point formatting, allowing more roundtrip checks to succeed. - [Vadim Evard](https://github.com/Pipeliner) fixed a Markdown issue in the README. - [zerodefect](https://github.com/zerodefect) fixed a compiler warning. - [Kert](https://github.com/kaidokert) allowed to template the string type in the serialization and added the possibility to override the exceptional behavior. - [mark-99](https://github.com/mark-99) helped fixing an ICC error. - [Patrik Huber](https://github.com/patrikhuber) fixed links in the README file. - [johnfb](https://github.com/johnfb) found a bug in the implementation of CBOR's indefinite length strings. - [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager. - [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise. - [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback. - [agrianius](https://github.com/agrianius) allowed to dump JSON to an alternative string type. - [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake. - [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io). - [Carlos O'Ryan](https://github.com/coryan) fixed a typo. - [James Upjohn](https://github.com/jammehcow) fixed a version number in the compilers section. - [Chuck Atkins](https://github.com/chuckatkins) adjusted the CMake files to the CMake packaging guidelines and provided documentation for the CMake integration. - [Jan Schöppach](https://github.com/dns13) fixed a typo. - [martin-mfg](https://github.com/martin-mfg) fixed a typo. - [Matthias Möller](https://github.com/TinyTinni) removed the dependency from `std::stringstream`. - [agrianius](https://github.com/agrianius) added code to use alternative string implementations. - [Daniel599](https://github.com/Daniel599) allowed to use more algorithms with the `items()` function. - [Julius Rakow](https://github.com/jrakow) fixed the Meson include directory and fixed the links to [cppreference.com](cppreference.com). - [Sonu Lohani](https://github.com/sonulohani) fixed the compilation with MSVC 2015 in debug mode. - [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases. - [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library. - [thyu](https://github.com/thyu) fixed a compiler warning. - [David Guthrie](https://github.com/LEgregius) fixed a subtle compilation error with Clang 3.4.2. - [Dennis Fischer](https://github.com/dennisfischer) allowed to call `find_package` without installing the library. - [Hyeon Kim](https://github.com/simnalamburt) fixed an issue with a double macro definition. - [Ben Berman](https://github.com/rivertam) made some error messages more understandable. - [zakalibit](https://github.com/zakalibit) fixed a compilation problem with the Intel C++ compiler. - [mandreyel](https://github.com/mandreyel) fixed a compilation problem. - [Kostiantyn Ponomarenko](https://github.com/koponomarenko) added version and license information to the Meson build file. - [Henry Schreiner](https://github.com/henryiii) added support for GCC 4.8. - [knilch](https://github.com/knilch0r) made sure the test suite does not stall when run in the wrong directory. - [Antonio Borondo](https://github.com/antonioborondo) fixed an MSVC 2017 warning. - [Dan Gendreau](https://github.com/dgendreau) implemented the `NLOHMANN_JSON_SERIALIZE_ENUM` macro to quickly define a enum/JSON mapping. - [efp](https://github.com/efp) added line and column information to parse errors. - [julian-becker](https://github.com/julian-becker) added BSON support. - [Pratik Chowdhury](https://github.com/pratikpc) added support for structured bindings. - [David Avedissian](https://github.com/davedissian) added support for Clang 5.0.1 (PS4 version). - [Jonathan Dumaresq](https://github.com/dumarjo) implemented an input adapter to read from `FILE*`. - [kjpus](https://github.com/kjpus) fixed a link in the documentation. - [Manvendra Singh](https://github.com/manu-chroma) fixed a typo in the documentation. - [ziggurat29](https://github.com/ziggurat29) fixed an MSVC warning. - [Sylvain Corlay](https://github.com/SylvainCorlay) added code to avoid an issue with MSVC. - [mefyl](https://github.com/mefyl) fixed a bug when JSON was parsed from an input stream. - [Millian Poquet](https://github.com/mpoquet) allowed to install the library via Meson. - [Michael Behrns-Miller](https://github.com/moodboom) found an issue with a missing namespace. - [Nasztanovics Ferenc](https://github.com/naszta) fixed a compilation issue with libc 2.12. - [Andreas Schwab](https://github.com/andreas-schwab) fixed the endian conversion. - [Mark-Dunning](https://github.com/Mark-Dunning) fixed a warning in MSVC. - [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) added `operator/` for JSON Pointers. - [John-Mark](https://github.com/johnmarkwayve) noted a missing header. - [Vitaly Zaitsev](https://github.com/xvitaly) fixed compilation with GCC 9.0. - [Laurent Stacul](https://github.com/stac47) fixed compilation with GCC 9.0. - [Ivor Wanders](https://github.com/iwanders) helped reducing the CMake requirement to version 3.1. - [njlr](https://github.com/njlr) updated the Buckaroo instructions. - [Lion](https://github.com/lieff) fixed a compilation issue with GCC 7 on CentOS. - [Isaac Nickaein](https://github.com/nickaein) improved the integer serialization performance and implemented the `contains()` function. - [past-due](https://github.com/past-due) suppressed an unfixable warning. - [Elvis Oric](https://github.com/elvisoric) improved Meson support. - [Matěj Plch](https://github.com/Afforix) fixed an example in the README. - [Mark Beckwith](https://github.com/wythe) fixed a typo. - [scinart](https://github.com/scinart) fixed bug in the serializer. - [Patrick Boettcher](https://github.com/pboettch) implemented `push_back()` and `pop_back()` for JSON Pointers. - [Bruno Oliveira](https://github.com/nicoddemus) added support for Conda. - [Michele Caini](https://github.com/skypjack) fixed links in the README. - [Hani](https://github.com/hnkb) documented how to install the library with NuGet. - [Mark Beckwith](https://github.com/wythe) fixed a typo. - [yann-morin-1998](https://github.com/yann-morin-1998) helped reducing the CMake requirement to version 3.1. - [Konstantin Podsvirov](https://github.com/podsvirov) maintains a package for the MSYS2 software distro. - [remyabel](https://github.com/remyabel) added GNUInstallDirs to the CMake files. - [Taylor Howard](https://github.com/taylorhoward92) fixed a unit test. - [Gabe Ron](https://github.com/Macr0Nerd) implemented the `to_string` method. - [Watal M. Iwasaki](https://github.com/heavywatal) fixed a Clang warning. - [Viktor Kirilov](https://github.com/onqtam) switched the unit tests from [Catch](https://github.com/philsquared/Catch) to [doctest](https://github.com/onqtam/doctest) - [Juncheng E](https://github.com/ejcjason) fixed a typo. - [tete17](https://github.com/tete17) fixed a bug in the `contains` function. - [Xav83](https://github.com/Xav83) fixed some cppcheck warnings. - [0xflotus](https://github.com/0xflotus) fixed some typos. - [Christian Deneke](https://github.com/chris0x44) added a const version of `json_pointer::back`. - [Julien Hamaide](https://github.com/crazyjul) made the `items()` function work with custom string types. - [Evan Nemerson](https://github.com/nemequ) updated fixed a bug in Hedley and updated this library accordingly. - [Florian Pigorsch](https://github.com/flopp) fixed a lot of typos. - [Camille Bégué](https://github.com/cbegue) fixed an issue in the conversion from `std::pair` and `std::tuple` to `json`. - [Anthony VH](https://github.com/AnthonyVH) fixed a compile error in an enum deserialization. - [Yuriy Vountesmery](https://github.com/ua-code-dragon) noted a subtle bug in a preprocessor check. - [Chen](https://github.com/dota17) fixed numerous issues in the library. - [Antony Kellermann](https://github.com/aokellermann) added a CI step for GCC 10.1. - [Alex](https://github.com/gistrec) fixed an MSVC warning. - [Rainer](https://github.com/rvjr) proposed an improvement in the floating-point serialization in CBOR. - [Francois Chabot](https://github.com/FrancoisChabot) made performance improvements in the input adapters. - [Arthur Sonzogni](https://github.com/ArthurSonzogni) documented how the library can be included via `FetchContent`. - [Rimas Misevičius](https://github.com/rmisev) fixed an error message. - [Alexander Myasnikov](https://github.com/alexandermyasnikov) fixed some examples and a link in the README. - [Hubert Chathi](https://github.com/uhoreg) made CMake's version config file architecture-independent. - [OmnipotentEntity](https://github.com/OmnipotentEntity) implemented the binary values for CBOR, MessagePack, BSON, and UBJSON. - [ArtemSarmini](https://github.com/ArtemSarmini) fixed a compilation issue with GCC 10 and fixed a leak. - [Evgenii Sopov](https://github.com/sea-kg) integrated the library to the wsjcpp package manager. - [Sergey Linev](https://github.com/linev) fixed a compiler warning. - [Miguel Magalhães](https://github.com/magamig) fixed the year in the copyright. - [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) fixed a compilation issue with MSVC. - [Alexander “weej” Jones](https://github.com/alex-weej) fixed an example in the README. - [Antoine Cœur](https://github.com/Coeur) fixed some typos in the documentation. - [jothepro](https://github.com/jothepro) updated links to the Hunter package. - [Dave Lee](https://github.com/kastiglione) fixed link in the README. - [Joël Lamotte](https://github.com/Klaim) added instruction for using Build2's package manager. - [Paul Jurczak](https://github.com/pauljurczak) fixed an example in the README. - [Sonu Lohani](https://github.com/sonulohani) fixed a warning. - [Carlos Gomes Martinho](https://github.com/gocarlos) updated the Conan package source. - [Konstantin Podsvirov](https://github.com/podsvirov) fixed the MSYS2 package documentation. - [Tridacnid](https://github.com/Tridacnid) improved the CMake tests. - [Michael](https://github.com/MBalszun) fixed MSVC warnings. - [Quentin Barbarat](https://github.com/quentin-dev) fixed an example in the documentation. - [XyFreak](https://github.com/XyFreak) fixed a compiler warning. - [TotalCaesar659](https://github.com/TotalCaesar659) fixed links in the README. - [Tanuj Garg](https://github.com/tanuj208) improved the fuzzer coverage for UBSAN input. - [AODQ](https://github.com/AODQ) fixed a compiler warning. - [jwittbrodt](https://github.com/jwittbrodt) made `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` inline. - [pfeatherstone](https://github.com/pfeatherstone) improved the upper bound of arguments of the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros. - [Jan Procházka](https://github.com/jprochazk) fixed a bug in the CBOR parser for binary and string values. - [T0b1-iOS](https://github.com/T0b1-iOS) fixed a bug in the new hash implementation. - [Matthew Bauer](https://github.com/matthewbauer) adjusted the CBOR writer to create tags for binary subtypes. - [gatopeich](https://github.com/gatopeich) implemented an ordered map container for `nlohmann::ordered_json`. - [Érico Nogueira Rolim](https://github.com/ericonr) added support for pkg-config. - [KonanM](https://github.com/KonanM) proposed an implementation for the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros. - [Guillaume Racicot](https://github.com/gracicot) implemented `string_view` support and allowed C++20 support. - [Alex Reinking](https://github.com/alexreinking) improved CMake support for `FetchContent`. - [Hannes Domani](https://github.com/ssbssa) provided a GDB pretty printer. - Lars Wirzenius reviewed the README file. - [Jun Jie](https://github.com/ongjunjie) fixed a compiler path in the CMake scripts. - [Ronak Buch](https://github.com/rbuch) fixed typos in the documentation. - [Alexander Karzhenkov](https://github.com/karzhenkov) fixed a move constructor and the Travis builds. - [Leonardo Lima](https://github.com/leozz37) added CPM.Cmake support. - [Joseph Blackman](https://github.com/jbzdarkid) fixed a warning. - [Yaroslav](https://github.com/YarikTH) updated doctest and implemented unit tests. - [Martin Stump](https://github.com/globberwops) fixed a bug in the CMake files. - [Jaakko Moisio](https://github.com/jasujm) fixed a bug in the input adapters. - [bl-ue](https://github.com/bl-ue) fixed some Markdown issues in the README file. - [William A. Wieselquist](https://github.com/wawiesel) fixed an example from the README. - [abbaswasim](https://github.com/abbaswasim) fixed an example from the README. - [Remy Jette](https://github.com/remyjette) fixed a warning. - [Fraser](https://github.com/frasermarlow) fixed the documentation. - [Ben Beasley](https://github.com/musicinmybrain) updated doctest. - [Doron Behar](https://github.com/doronbehar) fixed pkg-config.pc. - [raduteo](https://github.com/raduteo) fixed a warning. - [David Pfahler](https://github.com/theShmoo) added the possibility to compile the library without I/O support. - [Morten Fyhn Amundsen](https://github.com/mortenfyhn) fixed a typo. - [jpl-mac](https://github.com/jpl-mac) allowed to treat the library as a system header in CMake. - [Jason Dsouza](https://github.com/jasmcaus) fixed the indentation of the CMake file. - [offa](https://github.com/offa) added a link to Conan Center to the documentation. - [TotalCaesar659](https://github.com/TotalCaesar659) updated the links in the documentation to use HTTPS. - [Rafail Giavrimis](https://github.com/grafail) fixed the Google Benchmark default branch. - [Louis Dionne](https://github.com/ldionne) fixed a conversion operator. - [justanotheranonymoususer](https://github.com/justanotheranonymoususer) made the examples in the README more consistent. - [Finkman](https://github.com/Finkman) suppressed some `-Wfloat-equal` warnings. - [Ferry Huberts](https://github.com/fhuberts) fixed `-Wswitch-enum` warnings. - [Arseniy Terekhin](https://github.com/senyai) made the GDB pretty-printer robust against unset variable names. - [Amir Masoud Abdol](https://github.com/amirmasoudabdol) updated the Homebrew command as nlohmann/json is now in homebrew-core. - [Hallot](https://github.com/Hallot) fixed some `-Wextra-semi-stmt warnings`. - [Giovanni Cerretani](https://github.com/gcerretani) fixed `-Wunused` warnings on `JSON_DIAGNOSTICS`. - [Bogdan Popescu](https://github.com/Kapeli) hosts the [docset](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B) for offline documentation viewers. - [Carl Smedstad](https://github.com/carlsmedstad) fixed an assertion error when using `JSON_DIAGNOSTICS`. Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone. ## Used third-party tools The library itself consists of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of third-party tools and services. Thanks a lot! - [**amalgamate.py - Amalgamate C source and header files**](https://github.com/edlund/amalgamate) to create a single header file - [**American fuzzy lop**](https://lcamtuf.coredump.cx/afl/) for fuzz testing - [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows - [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code indentation - [**Clang**](https://clang.llvm.org) for compilation with code sanitizers - [**CMake**](https://cmake.org) for build automation - [**Codacity**](https://www.codacy.com) for further [code analysis](https://www.codacy.com/app/nlohmann/json) - [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json) - [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json) - [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis - [**doctest**](https://github.com/onqtam/doctest) for the unit tests - [**Doxygen**](https://www.doxygen.nl/index.html) to generate [documentation](https://nlohmann.github.io/json/doxygen/index.html) - [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages - [**GitHub Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md) - [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks - [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros - [**lcov**](http://ltp.sourceforge.net/coverage/lcov.php) to process coverage information and create a HTML view - [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz - [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json)) - [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments. - [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](https://wandbox.org) - [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS - [**Valgrind**](https://valgrind.org) to check for correct memory management - [**Wandbox**](https://wandbox.org) for [online examples](https://wandbox.org/permlink/1mp10JbaANo6FUc7) ## Projects using JSON for Modern C++ The library is currently used in Apple macOS Sierra and iOS 10. I am not sure what they are using the library for, but I am happy that it runs on so many devices. ## Notes ### Character encoding The library supports **Unicode input** as follows: - Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1). - `std::u16string` and `std::u32string` can be parsed, assuming UTF-16 and UTF-32 encoding, respectively. These encodings are not supported when reading from files or other input containers. - Other encodings such as Latin-1 or ISO 8859-1 are **not** supported and will yield parse or serialization errors. - [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library. - Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors. - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs. - When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. - To store wide strings (e.g., `std::wstring`), you need to convert them to a a UTF-8 encoded `std::string` before, see [an example](https://json.nlohmann.me/home/faq/#wide-string-handling). ### Comments in JSON This library does not support comments by default. It does so for three reasons: 1. Comments are not part of the [JSON specification](https://tools.ietf.org/html/rfc8259). You may argue that `//` or `/* */` are allowed in JavaScript, but JSON is not JavaScript. 2. This was not an oversight: Douglas Crockford [wrote on this](https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) in May 2012: > I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't. > Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser. 3. It is dangerous for interoperability if some libraries would add comment support while others don't. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this. However, you can pass set parameter `ignore_comments` to true in the `parse` function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace. ### Order of object keys By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs". If you do want to preserve the insertion order, you can try the type [`nlohmann::ordered_json`](https://github.com/nlohmann/json/issues/2179). Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)). ### Memory Release We checked with Valgrind and the Address Sanitizer (ASAN) that there are no memory leaks. If you find that a parsing program with this library does not release memory, please consider the following case and it maybe unrelated to this library. **Your program is compiled with glibc.** There is a tunable threshold that glibc uses to decide whether to actually return memory to the system or whether to cache it for later reuse. If in your program you make lots of small allocations and those small allocations are not a contiguous block and are presumably below the threshold, then they will not get returned to the OS. Here is a related issue [#1924](https://github.com/nlohmann/json/issues/1924). ### Further notes - The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/api/basic_json/operator%5B%5D/) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/api/basic_json/at/). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`. - As the exact type of a number is not defined in the [JSON specification](https://tools.ietf.org/html/rfc8259.html), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions. - The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag. - **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER` (overriding `throw`), `JSON_TRY_USER` (overriding `try`), and `JSON_CATCH_USER` (overriding `catch`). Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. Note the explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824). ## Execute unit tests To compile and run the tests, you need to execute ```sh $ mkdir build $ cd build $ cmake .. -DJSON_BuildTests=On $ cmake --build . $ ctest --output-on-failure ``` Note that during the `ctest` stage, several JSON test files are downloaded from an [external repository](https://github.com/nlohmann/json_test_data). If policies forbid downloading artifacts during testing, you can download the files yourself and pass the directory with the test files via `-DJSON_TestDataDirectory=path` to CMake. Then, no Internet connectivity is required. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure` will fail. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information. Note you need to call `cmake -LE "not_reproducible|git_required"` to exclude both labels. See [issue #2596](https://github.com/nlohmann/json/issues/2596) for more information. As Intel compilers use unsafe floating point optimization by default, the unit tests may fail. Use flag [`/fp:precise`](https://software.intel.com/content/www/us/en/develop/documentation/cpp-compiler-developer-guide-and-reference/top/compiler-reference/compiler-options/compiler-option-details/floating-point-options/fp-model-fp.html) then. ================================================ FILE: d2mapapi/json/json.hpp ================================================ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ | | |__ | | | | | | version 3.10.4 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . SPDX-License-Identifier: MIT Copyright (c) 2013-2019 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ #define NLOHMANN_JSON_VERSION_MAJOR 3 #define NLOHMANN_JSON_VERSION_MINOR 10 #define NLOHMANN_JSON_VERSION_PATCH 4 #include // all_of, find, for_each #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list #ifndef JSON_NO_IO #include // istream, ostream #endif // JSON_NO_IO #include // random_access_iterator_tag #include // unique_ptr #include // accumulate #include // string, stoi, to_string #include // declval, forward, move, pair, swap #include // vector // #include #include #include // #include #include // transform #include // array #include // forward_list #include // inserter, front_inserter, end #include // map #include // string #include // tuple, make_tuple #include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible #include // unordered_map #include // pair, declval #include // valarray // #include #include // exception #include // runtime_error #include // to_string #include // vector // #include #include // array #include // size_t #include // uint8_t #include // string namespace nlohmann { namespace detail { /////////////////////////// // JSON type enumeration // /////////////////////////// /*! @brief the JSON type enumeration This enumeration collects the different JSON types. It is internally used to distinguish the stored values, and the functions @ref basic_json::is_null(), @ref basic_json::is_object(), @ref basic_json::is_array(), @ref basic_json::is_string(), @ref basic_json::is_boolean(), @ref basic_json::is_number() (with @ref basic_json::is_number_integer(), @ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), @ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and @ref basic_json::is_structured() rely on it. @note There are three enumeration entries (number_integer, number_unsigned, and number_float), because the library distinguishes these three types for numbers: @ref basic_json::number_unsigned_t is used for unsigned integers, @ref basic_json::number_integer_t is used for signed integers, and @ref basic_json::number_float_t is used for floating-point numbers or to approximate integers which do not fit in the limits of their respective type. @sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON value with the default value for a given type @since version 1.0.0 */ enum class value_t : std::uint8_t { null, ///< null value object, ///< object (unordered set of name/value pairs) array, ///< array (ordered collection of values) string, ///< string value boolean, ///< boolean value number_integer, ///< number value (signed integer) number_unsigned, ///< number value (unsigned integer) number_float, ///< number value (floating-point) binary, ///< binary array (ordered collection of bytes) discarded ///< discarded by the parser callback function }; /*! @brief comparison operator for JSON types Returns an ordering that is similar to Python: - order: null < boolean < number < object < array < string < binary - furthermore, each type is not smaller than itself - discarded values are not comparable - binary is represented as a b"" string in python and directly comparable to a string; however, making a binary array directly comparable with a string would be surprising behavior in a JSON file. @since version 1.0.0 */ inline bool operator<(const value_t lhs, const value_t rhs) noexcept { static constexpr std::array order = {{ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, 6 /* binary */ } }; const auto l_index = static_cast(lhs); const auto r_index = static_cast(rhs); return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; } } // namespace detail } // namespace nlohmann // #include #include // #include #include // declval, pair // #include /* Hedley - https://nemequ.github.io/hedley * Created by Evan Nemerson * * To the extent possible under law, the author(s) have dedicated all * copyright and related and neighboring rights to this software to * the public domain worldwide. This software is distributed without * any warranty. * * For details, see . * SPDX-License-Identifier: CC0-1.0 */ #if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) #if defined(JSON_HEDLEY_VERSION) #undef JSON_HEDLEY_VERSION #endif #define JSON_HEDLEY_VERSION 15 #if defined(JSON_HEDLEY_STRINGIFY_EX) #undef JSON_HEDLEY_STRINGIFY_EX #endif #define JSON_HEDLEY_STRINGIFY_EX(x) #x #if defined(JSON_HEDLEY_STRINGIFY) #undef JSON_HEDLEY_STRINGIFY #endif #define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) #if defined(JSON_HEDLEY_CONCAT_EX) #undef JSON_HEDLEY_CONCAT_EX #endif #define JSON_HEDLEY_CONCAT_EX(a,b) a##b #if defined(JSON_HEDLEY_CONCAT) #undef JSON_HEDLEY_CONCAT #endif #define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) #if defined(JSON_HEDLEY_CONCAT3_EX) #undef JSON_HEDLEY_CONCAT3_EX #endif #define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c #if defined(JSON_HEDLEY_CONCAT3) #undef JSON_HEDLEY_CONCAT3 #endif #define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) #if defined(JSON_HEDLEY_VERSION_ENCODE) #undef JSON_HEDLEY_VERSION_ENCODE #endif #define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) #if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) #undef JSON_HEDLEY_VERSION_DECODE_MAJOR #endif #define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) #if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) #undef JSON_HEDLEY_VERSION_DECODE_MINOR #endif #define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) #if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) #undef JSON_HEDLEY_VERSION_DECODE_REVISION #endif #define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) #if defined(JSON_HEDLEY_GNUC_VERSION) #undef JSON_HEDLEY_GNUC_VERSION #endif #if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #elif defined(__GNUC__) #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) #endif #if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) #undef JSON_HEDLEY_GNUC_VERSION_CHECK #endif #if defined(JSON_HEDLEY_GNUC_VERSION) #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_MSVC_VERSION) #undef JSON_HEDLEY_MSVC_VERSION #endif #if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) #elif defined(_MSC_FULL_VER) && !defined(__ICL) #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) #elif defined(_MSC_VER) && !defined(__ICL) #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) #endif #if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) #undef JSON_HEDLEY_MSVC_VERSION_CHECK #endif #if !defined(JSON_HEDLEY_MSVC_VERSION) #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) #elif defined(_MSC_VER) && (_MSC_VER >= 1400) #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) #elif defined(_MSC_VER) && (_MSC_VER >= 1200) #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) #else #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) #endif #if defined(JSON_HEDLEY_INTEL_VERSION) #undef JSON_HEDLEY_INTEL_VERSION #endif #if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) #elif defined(__INTEL_COMPILER) && !defined(__ICL) #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) #endif #if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) #undef JSON_HEDLEY_INTEL_VERSION_CHECK #endif #if defined(JSON_HEDLEY_INTEL_VERSION) #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_INTEL_CL_VERSION) #undef JSON_HEDLEY_INTEL_CL_VERSION #endif #if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) #endif #if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK #endif #if defined(JSON_HEDLEY_INTEL_CL_VERSION) #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_PGI_VERSION) #undef JSON_HEDLEY_PGI_VERSION #endif #if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) #endif #if defined(JSON_HEDLEY_PGI_VERSION_CHECK) #undef JSON_HEDLEY_PGI_VERSION_CHECK #endif #if defined(JSON_HEDLEY_PGI_VERSION) #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_SUNPRO_VERSION) #undef JSON_HEDLEY_SUNPRO_VERSION #endif #if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) #elif defined(__SUNPRO_C) #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) #elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) #elif defined(__SUNPRO_CC) #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) #endif #if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK #endif #if defined(JSON_HEDLEY_SUNPRO_VERSION) #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) #undef JSON_HEDLEY_EMSCRIPTEN_VERSION #endif #if defined(__EMSCRIPTEN__) #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) #endif #if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK #endif #if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_ARM_VERSION) #undef JSON_HEDLEY_ARM_VERSION #endif #if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) #elif defined(__CC_ARM) && defined(__ARMCC_VERSION) #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) #endif #if defined(JSON_HEDLEY_ARM_VERSION_CHECK) #undef JSON_HEDLEY_ARM_VERSION_CHECK #endif #if defined(JSON_HEDLEY_ARM_VERSION) #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_IBM_VERSION) #undef JSON_HEDLEY_IBM_VERSION #endif #if defined(__ibmxl__) #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) #elif defined(__xlC__) && defined(__xlC_ver__) #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) #elif defined(__xlC__) #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) #endif #if defined(JSON_HEDLEY_IBM_VERSION_CHECK) #undef JSON_HEDLEY_IBM_VERSION_CHECK #endif #if defined(JSON_HEDLEY_IBM_VERSION) #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_TI_VERSION) #undef JSON_HEDLEY_TI_VERSION #endif #if \ defined(__TI_COMPILER_VERSION__) && \ ( \ defined(__TMS470__) || defined(__TI_ARM__) || \ defined(__MSP430__) || \ defined(__TMS320C2000__) \ ) #if (__TI_COMPILER_VERSION__ >= 16000000) #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #endif #if defined(JSON_HEDLEY_TI_VERSION_CHECK) #undef JSON_HEDLEY_TI_VERSION_CHECK #endif #if defined(JSON_HEDLEY_TI_VERSION) #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_TI_CL2000_VERSION) #undef JSON_HEDLEY_TI_CL2000_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK #endif #if defined(JSON_HEDLEY_TI_CL2000_VERSION) #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_TI_CL430_VERSION) #undef JSON_HEDLEY_TI_CL430_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK #endif #if defined(JSON_HEDLEY_TI_CL430_VERSION) #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_TI_ARMCL_VERSION) #undef JSON_HEDLEY_TI_ARMCL_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK #endif #if defined(JSON_HEDLEY_TI_ARMCL_VERSION) #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_TI_CL6X_VERSION) #undef JSON_HEDLEY_TI_CL6X_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK #endif #if defined(JSON_HEDLEY_TI_CL6X_VERSION) #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_TI_CL7X_VERSION) #undef JSON_HEDLEY_TI_CL7X_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK #endif #if defined(JSON_HEDLEY_TI_CL7X_VERSION) #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_TI_CLPRU_VERSION) #undef JSON_HEDLEY_TI_CLPRU_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK #endif #if defined(JSON_HEDLEY_TI_CLPRU_VERSION) #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_CRAY_VERSION) #undef JSON_HEDLEY_CRAY_VERSION #endif #if defined(_CRAYC) #if defined(_RELEASE_PATCHLEVEL) #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) #else #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) #endif #endif #if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) #undef JSON_HEDLEY_CRAY_VERSION_CHECK #endif #if defined(JSON_HEDLEY_CRAY_VERSION) #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_IAR_VERSION) #undef JSON_HEDLEY_IAR_VERSION #endif #if defined(__IAR_SYSTEMS_ICC__) #if __VER__ > 1000 #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) #else #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) #endif #endif #if defined(JSON_HEDLEY_IAR_VERSION_CHECK) #undef JSON_HEDLEY_IAR_VERSION_CHECK #endif #if defined(JSON_HEDLEY_IAR_VERSION) #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_TINYC_VERSION) #undef JSON_HEDLEY_TINYC_VERSION #endif #if defined(__TINYC__) #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) #endif #if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) #undef JSON_HEDLEY_TINYC_VERSION_CHECK #endif #if defined(JSON_HEDLEY_TINYC_VERSION) #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_DMC_VERSION) #undef JSON_HEDLEY_DMC_VERSION #endif #if defined(__DMC__) #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) #endif #if defined(JSON_HEDLEY_DMC_VERSION_CHECK) #undef JSON_HEDLEY_DMC_VERSION_CHECK #endif #if defined(JSON_HEDLEY_DMC_VERSION) #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_COMPCERT_VERSION) #undef JSON_HEDLEY_COMPCERT_VERSION #endif #if defined(__COMPCERT_VERSION__) #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) #endif #if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK #endif #if defined(JSON_HEDLEY_COMPCERT_VERSION) #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_PELLES_VERSION) #undef JSON_HEDLEY_PELLES_VERSION #endif #if defined(__POCC__) #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) #endif #if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) #undef JSON_HEDLEY_PELLES_VERSION_CHECK #endif #if defined(JSON_HEDLEY_PELLES_VERSION) #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_MCST_LCC_VERSION) #undef JSON_HEDLEY_MCST_LCC_VERSION #endif #if defined(__LCC__) && defined(__LCC_MINOR__) #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) #endif #if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK #endif #if defined(JSON_HEDLEY_MCST_LCC_VERSION) #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_GCC_VERSION) #undef JSON_HEDLEY_GCC_VERSION #endif #if \ defined(JSON_HEDLEY_GNUC_VERSION) && \ !defined(__clang__) && \ !defined(JSON_HEDLEY_INTEL_VERSION) && \ !defined(JSON_HEDLEY_PGI_VERSION) && \ !defined(JSON_HEDLEY_ARM_VERSION) && \ !defined(JSON_HEDLEY_CRAY_VERSION) && \ !defined(JSON_HEDLEY_TI_VERSION) && \ !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ !defined(__COMPCERT__) && \ !defined(JSON_HEDLEY_MCST_LCC_VERSION) #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION #endif #if defined(JSON_HEDLEY_GCC_VERSION_CHECK) #undef JSON_HEDLEY_GCC_VERSION_CHECK #endif #if defined(JSON_HEDLEY_GCC_VERSION) #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) #else #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(JSON_HEDLEY_HAS_ATTRIBUTE) #undef JSON_HEDLEY_HAS_ATTRIBUTE #endif #if \ defined(__has_attribute) && \ ( \ (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ ) # define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) #else # define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) #endif #if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE #endif #if defined(__has_attribute) #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) #else #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE #endif #if defined(__has_attribute) #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) #else #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE #endif #if \ defined(__has_cpp_attribute) && \ defined(__cplusplus) && \ (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) #else #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) #endif #if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS #endif #if !defined(__cplusplus) || !defined(__has_cpp_attribute) #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) #elif \ !defined(JSON_HEDLEY_PGI_VERSION) && \ !defined(JSON_HEDLEY_IAR_VERSION) && \ (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) #else #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) #endif #if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE #endif #if defined(__has_cpp_attribute) && defined(__cplusplus) #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) #else #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE #endif #if defined(__has_cpp_attribute) && defined(__cplusplus) #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) #else #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_HAS_BUILTIN) #undef JSON_HEDLEY_HAS_BUILTIN #endif #if defined(__has_builtin) #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) #else #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) #endif #if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) #undef JSON_HEDLEY_GNUC_HAS_BUILTIN #endif #if defined(__has_builtin) #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) #else #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) #undef JSON_HEDLEY_GCC_HAS_BUILTIN #endif #if defined(__has_builtin) #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) #else #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_HAS_FEATURE) #undef JSON_HEDLEY_HAS_FEATURE #endif #if defined(__has_feature) #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) #else #define JSON_HEDLEY_HAS_FEATURE(feature) (0) #endif #if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) #undef JSON_HEDLEY_GNUC_HAS_FEATURE #endif #if defined(__has_feature) #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) #else #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_GCC_HAS_FEATURE) #undef JSON_HEDLEY_GCC_HAS_FEATURE #endif #if defined(__has_feature) #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) #else #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_HAS_EXTENSION) #undef JSON_HEDLEY_HAS_EXTENSION #endif #if defined(__has_extension) #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) #else #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) #endif #if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) #undef JSON_HEDLEY_GNUC_HAS_EXTENSION #endif #if defined(__has_extension) #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) #else #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) #undef JSON_HEDLEY_GCC_HAS_EXTENSION #endif #if defined(__has_extension) #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) #else #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) #else #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) #endif #if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) #else #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) #else #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_HAS_WARNING) #undef JSON_HEDLEY_HAS_WARNING #endif #if defined(__has_warning) #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) #else #define JSON_HEDLEY_HAS_WARNING(warning) (0) #endif #if defined(JSON_HEDLEY_GNUC_HAS_WARNING) #undef JSON_HEDLEY_GNUC_HAS_WARNING #endif #if defined(__has_warning) #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) #else #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_GCC_HAS_WARNING) #undef JSON_HEDLEY_GCC_HAS_WARNING #endif #if defined(__has_warning) #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) #else #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ defined(__clang__) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) #elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) #define JSON_HEDLEY_PRAGMA(value) __pragma(value) #else #define JSON_HEDLEY_PRAGMA(value) #endif #if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) #undef JSON_HEDLEY_DIAGNOSTIC_PUSH #endif #if defined(JSON_HEDLEY_DIAGNOSTIC_POP) #undef JSON_HEDLEY_DIAGNOSTIC_POP #endif #if defined(__clang__) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") #elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") #elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) #elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") #elif \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") #elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") #else #define JSON_HEDLEY_DIAGNOSTIC_PUSH #define JSON_HEDLEY_DIAGNOSTIC_POP #endif /* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ #endif #if defined(__cplusplus) # if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") # if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") # if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") # define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ xpr \ JSON_HEDLEY_DIAGNOSTIC_POP # else # define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ xpr \ JSON_HEDLEY_DIAGNOSTIC_POP # endif # else # define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ xpr \ JSON_HEDLEY_DIAGNOSTIC_POP # endif # endif #endif #if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x #endif #if defined(JSON_HEDLEY_CONST_CAST) #undef JSON_HEDLEY_CONST_CAST #endif #if defined(__cplusplus) # define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) #elif \ JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ ((T) (expr)); \ JSON_HEDLEY_DIAGNOSTIC_POP \ })) #else # define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) #endif #if defined(JSON_HEDLEY_REINTERPRET_CAST) #undef JSON_HEDLEY_REINTERPRET_CAST #endif #if defined(__cplusplus) #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) #else #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) #endif #if defined(JSON_HEDLEY_STATIC_CAST) #undef JSON_HEDLEY_STATIC_CAST #endif #if defined(__cplusplus) #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) #else #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) #endif #if defined(JSON_HEDLEY_CPP_CAST) #undef JSON_HEDLEY_CPP_CAST #endif #if defined(__cplusplus) # if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") # define JSON_HEDLEY_CPP_CAST(T, expr) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ ((T) (expr)) \ JSON_HEDLEY_DIAGNOSTIC_POP # elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) # define JSON_HEDLEY_CPP_CAST(T, expr) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("diag_suppress=Pe137") \ JSON_HEDLEY_DIAGNOSTIC_POP # else # define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) # endif #else # define JSON_HEDLEY_CPP_CAST(T, expr) (expr) #endif #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED #endif #if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") #elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") #elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) #elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") #elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") #elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) #elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") #elif \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") #elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") #else #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED #endif #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS #endif #if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") #elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") #elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) #elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") #elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") #elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) #elif \ JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") #elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") #elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") #else #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS #endif #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES #endif #if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") #elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") #elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) #elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) #elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") #elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") #elif \ JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") #elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") #else #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES #endif #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #endif #if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") #elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") #elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") #else #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #endif #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION #endif #if JSON_HEDLEY_HAS_WARNING("-Wunused-function") #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") #elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") #elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) #elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") #else #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION #endif #if defined(JSON_HEDLEY_DEPRECATED) #undef JSON_HEDLEY_DEPRECATED #endif #if defined(JSON_HEDLEY_DEPRECATED_FOR) #undef JSON_HEDLEY_DEPRECATED_FOR #endif #if \ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) #elif \ (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) #elif defined(__cplusplus) && (__cplusplus >= 201402L) #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) #elif \ JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") #else #define JSON_HEDLEY_DEPRECATED(since) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) #endif #if defined(JSON_HEDLEY_UNAVAILABLE) #undef JSON_HEDLEY_UNAVAILABLE #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) #else #define JSON_HEDLEY_UNAVAILABLE(available_since) #endif #if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) #undef JSON_HEDLEY_WARN_UNUSED_RESULT #endif #if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) #elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) #elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) #elif defined(_Check_return_) /* SAL */ #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ #else #define JSON_HEDLEY_WARN_UNUSED_RESULT #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) #endif #if defined(JSON_HEDLEY_SENTINEL) #undef JSON_HEDLEY_SENTINEL #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) #else #define JSON_HEDLEY_SENTINEL(position) #endif #if defined(JSON_HEDLEY_NO_RETURN) #undef JSON_HEDLEY_NO_RETURN #endif #if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_NO_RETURN __noreturn #elif \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L #define JSON_HEDLEY_NO_RETURN _Noreturn #elif defined(__cplusplus) && (__cplusplus >= 201103L) #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) #elif \ JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) #elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") #elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) #elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) #else #define JSON_HEDLEY_NO_RETURN #endif #if defined(JSON_HEDLEY_NO_ESCAPE) #undef JSON_HEDLEY_NO_ESCAPE #endif #if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) #else #define JSON_HEDLEY_NO_ESCAPE #endif #if defined(JSON_HEDLEY_UNREACHABLE) #undef JSON_HEDLEY_UNREACHABLE #endif #if defined(JSON_HEDLEY_UNREACHABLE_RETURN) #undef JSON_HEDLEY_UNREACHABLE_RETURN #endif #if defined(JSON_HEDLEY_ASSUME) #undef JSON_HEDLEY_ASSUME #endif #if \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_ASSUME(expr) __assume(expr) #elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) #elif \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) #if defined(__cplusplus) #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) #else #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) #endif #endif #if \ (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() #elif defined(JSON_HEDLEY_ASSUME) #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) #endif #if !defined(JSON_HEDLEY_ASSUME) #if defined(JSON_HEDLEY_UNREACHABLE) #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) #else #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) #endif #endif #if defined(JSON_HEDLEY_UNREACHABLE) #if \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) #else #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() #endif #else #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) #endif #if !defined(JSON_HEDLEY_UNREACHABLE) #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) #endif JSON_HEDLEY_DIAGNOSTIC_PUSH #if JSON_HEDLEY_HAS_WARNING("-Wpedantic") #pragma clang diagnostic ignored "-Wpedantic" #endif #if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #endif #if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) #if defined(__clang__) #pragma clang diagnostic ignored "-Wvariadic-macros" #elif defined(JSON_HEDLEY_GCC_VERSION) #pragma GCC diagnostic ignored "-Wvariadic-macros" #endif #endif #if defined(JSON_HEDLEY_NON_NULL) #undef JSON_HEDLEY_NON_NULL #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) #else #define JSON_HEDLEY_NON_NULL(...) #endif JSON_HEDLEY_DIAGNOSTIC_POP #if defined(JSON_HEDLEY_PRINTF_FORMAT) #undef JSON_HEDLEY_PRINTF_FORMAT #endif #if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) #elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) #elif \ JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) #elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) #else #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) #endif #if defined(JSON_HEDLEY_CONSTEXPR) #undef JSON_HEDLEY_CONSTEXPR #endif #if defined(__cplusplus) #if __cplusplus >= 201103L #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) #endif #endif #if !defined(JSON_HEDLEY_CONSTEXPR) #define JSON_HEDLEY_CONSTEXPR #endif #if defined(JSON_HEDLEY_PREDICT) #undef JSON_HEDLEY_PREDICT #endif #if defined(JSON_HEDLEY_LIKELY) #undef JSON_HEDLEY_LIKELY #endif #if defined(JSON_HEDLEY_UNLIKELY) #undef JSON_HEDLEY_UNLIKELY #endif #if defined(JSON_HEDLEY_UNPREDICTABLE) #undef JSON_HEDLEY_UNPREDICTABLE #endif #if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) #endif #if \ (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) # define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) # define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) # define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) # define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) #elif \ (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define JSON_HEDLEY_PREDICT(expr, expected, probability) \ (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) # define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ (__extension__ ({ \ double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ })) # define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ (__extension__ ({ \ double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ })) # define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) # define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) #else # define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) # define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) # define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) # define JSON_HEDLEY_LIKELY(expr) (!!(expr)) # define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) #endif #if !defined(JSON_HEDLEY_UNPREDICTABLE) #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) #endif #if defined(JSON_HEDLEY_MALLOC) #undef JSON_HEDLEY_MALLOC #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_MALLOC __declspec(restrict) #else #define JSON_HEDLEY_MALLOC #endif #if defined(JSON_HEDLEY_PURE) #undef JSON_HEDLEY_PURE #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define JSON_HEDLEY_PURE __attribute__((__pure__)) #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) # define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") #elif defined(__cplusplus) && \ ( \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ ) # define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") #else # define JSON_HEDLEY_PURE #endif #if defined(JSON_HEDLEY_CONST) #undef JSON_HEDLEY_CONST #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_CONST __attribute__((__const__)) #elif \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) #define JSON_HEDLEY_CONST _Pragma("no_side_effect") #else #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE #endif #if defined(JSON_HEDLEY_RESTRICT) #undef JSON_HEDLEY_RESTRICT #endif #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) #define JSON_HEDLEY_RESTRICT restrict #elif \ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ defined(__clang__) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_RESTRICT __restrict #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) #define JSON_HEDLEY_RESTRICT _Restrict #else #define JSON_HEDLEY_RESTRICT #endif #if defined(JSON_HEDLEY_INLINE) #undef JSON_HEDLEY_INLINE #endif #if \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ (defined(__cplusplus) && (__cplusplus >= 199711L)) #define JSON_HEDLEY_INLINE inline #elif \ defined(JSON_HEDLEY_GCC_VERSION) || \ JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) #define JSON_HEDLEY_INLINE __inline__ #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_INLINE __inline #else #define JSON_HEDLEY_INLINE #endif #if defined(JSON_HEDLEY_ALWAYS_INLINE) #undef JSON_HEDLEY_ALWAYS_INLINE #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) # define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define JSON_HEDLEY_ALWAYS_INLINE __forceinline #elif defined(__cplusplus) && \ ( \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ ) # define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) # define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") #else # define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE #endif #if defined(JSON_HEDLEY_NEVER_INLINE) #undef JSON_HEDLEY_NEVER_INLINE #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) #elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") #elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") #elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) #elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) #else #define JSON_HEDLEY_NEVER_INLINE #endif #if defined(JSON_HEDLEY_PRIVATE) #undef JSON_HEDLEY_PRIVATE #endif #if defined(JSON_HEDLEY_PUBLIC) #undef JSON_HEDLEY_PUBLIC #endif #if defined(JSON_HEDLEY_IMPORT) #undef JSON_HEDLEY_IMPORT #endif #if defined(_WIN32) || defined(__CYGWIN__) # define JSON_HEDLEY_PRIVATE # define JSON_HEDLEY_PUBLIC __declspec(dllexport) # define JSON_HEDLEY_IMPORT __declspec(dllimport) #else # if \ JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ ( \ defined(__TI_EABI__) && \ ( \ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ ) \ ) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) # define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) # else # define JSON_HEDLEY_PRIVATE # define JSON_HEDLEY_PUBLIC # endif # define JSON_HEDLEY_IMPORT extern #endif #if defined(JSON_HEDLEY_NO_THROW) #undef JSON_HEDLEY_NO_THROW #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) #define JSON_HEDLEY_NO_THROW __declspec(nothrow) #else #define JSON_HEDLEY_NO_THROW #endif #if defined(JSON_HEDLEY_FALL_THROUGH) #undef JSON_HEDLEY_FALL_THROUGH #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) #elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) #elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) #elif defined(__fallthrough) /* SAL */ #define JSON_HEDLEY_FALL_THROUGH __fallthrough #else #define JSON_HEDLEY_FALL_THROUGH #endif #if defined(JSON_HEDLEY_RETURNS_NON_NULL) #undef JSON_HEDLEY_RETURNS_NON_NULL #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) #elif defined(_Ret_notnull_) /* SAL */ #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ #else #define JSON_HEDLEY_RETURNS_NON_NULL #endif #if defined(JSON_HEDLEY_ARRAY_PARAM) #undef JSON_HEDLEY_ARRAY_PARAM #endif #if \ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ !defined(__STDC_NO_VLA__) && \ !defined(__cplusplus) && \ !defined(JSON_HEDLEY_PGI_VERSION) && \ !defined(JSON_HEDLEY_TINYC_VERSION) #define JSON_HEDLEY_ARRAY_PARAM(name) (name) #else #define JSON_HEDLEY_ARRAY_PARAM(name) #endif #if defined(JSON_HEDLEY_IS_CONSTANT) #undef JSON_HEDLEY_IS_CONSTANT #endif #if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) #undef JSON_HEDLEY_REQUIRE_CONSTEXPR #endif /* JSON_HEDLEY_IS_CONSTEXPR_ is for HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ #if defined(JSON_HEDLEY_IS_CONSTEXPR_) #undef JSON_HEDLEY_IS_CONSTEXPR_ #endif #if \ JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) #endif #if !defined(__cplusplus) # if \ JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) #if defined(__INTPTR_TYPE__) #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) #else #include #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) #endif # elif \ ( \ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ !defined(JSON_HEDLEY_PGI_VERSION) && \ !defined(JSON_HEDLEY_IAR_VERSION)) || \ (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) #if defined(__INTPTR_TYPE__) #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) #else #include #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) #endif # elif \ defined(JSON_HEDLEY_GCC_VERSION) || \ defined(JSON_HEDLEY_INTEL_VERSION) || \ defined(JSON_HEDLEY_TINYC_VERSION) || \ defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ defined(__clang__) # define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ sizeof(void) != \ sizeof(*( \ 1 ? \ ((void*) ((expr) * 0L) ) : \ ((struct { char v[sizeof(void) * 2]; } *) 1) \ ) \ ) \ ) # endif #endif #if defined(JSON_HEDLEY_IS_CONSTEXPR_) #if !defined(JSON_HEDLEY_IS_CONSTANT) #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) #endif #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) #else #if !defined(JSON_HEDLEY_IS_CONSTANT) #define JSON_HEDLEY_IS_CONSTANT(expr) (0) #endif #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) #endif #if defined(JSON_HEDLEY_BEGIN_C_DECLS) #undef JSON_HEDLEY_BEGIN_C_DECLS #endif #if defined(JSON_HEDLEY_END_C_DECLS) #undef JSON_HEDLEY_END_C_DECLS #endif #if defined(JSON_HEDLEY_C_DECL) #undef JSON_HEDLEY_C_DECL #endif #if defined(__cplusplus) #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { #define JSON_HEDLEY_END_C_DECLS } #define JSON_HEDLEY_C_DECL extern "C" #else #define JSON_HEDLEY_BEGIN_C_DECLS #define JSON_HEDLEY_END_C_DECLS #define JSON_HEDLEY_C_DECL #endif #if defined(JSON_HEDLEY_STATIC_ASSERT) #undef JSON_HEDLEY_STATIC_ASSERT #endif #if \ !defined(__cplusplus) && ( \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ defined(_Static_assert) \ ) # define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) #elif \ (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) #else # define JSON_HEDLEY_STATIC_ASSERT(expr, message) #endif #if defined(JSON_HEDLEY_NULL) #undef JSON_HEDLEY_NULL #endif #if defined(__cplusplus) #if __cplusplus >= 201103L #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) #elif defined(NULL) #define JSON_HEDLEY_NULL NULL #else #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) #endif #elif defined(NULL) #define JSON_HEDLEY_NULL NULL #else #define JSON_HEDLEY_NULL ((void*) 0) #endif #if defined(JSON_HEDLEY_MESSAGE) #undef JSON_HEDLEY_MESSAGE #endif #if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") # define JSON_HEDLEY_MESSAGE(msg) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ JSON_HEDLEY_PRAGMA(message msg) \ JSON_HEDLEY_DIAGNOSTIC_POP #elif \ JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) #elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) # define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) # define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) #elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) # define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) #else # define JSON_HEDLEY_MESSAGE(msg) #endif #if defined(JSON_HEDLEY_WARNING) #undef JSON_HEDLEY_WARNING #endif #if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") # define JSON_HEDLEY_WARNING(msg) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ JSON_HEDLEY_PRAGMA(clang warning msg) \ JSON_HEDLEY_DIAGNOSTIC_POP #elif \ JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) #else # define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) #endif #if defined(JSON_HEDLEY_REQUIRE) #undef JSON_HEDLEY_REQUIRE #endif #if defined(JSON_HEDLEY_REQUIRE_MSG) #undef JSON_HEDLEY_REQUIRE_MSG #endif #if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) # if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") # define JSON_HEDLEY_REQUIRE(expr) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ __attribute__((diagnose_if(!(expr), #expr, "error"))) \ JSON_HEDLEY_DIAGNOSTIC_POP # define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ __attribute__((diagnose_if(!(expr), msg, "error"))) \ JSON_HEDLEY_DIAGNOSTIC_POP # else # define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) # define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) # endif #else # define JSON_HEDLEY_REQUIRE(expr) # define JSON_HEDLEY_REQUIRE_MSG(expr,msg) #endif #if defined(JSON_HEDLEY_FLAGS) #undef JSON_HEDLEY_FLAGS #endif #if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) #else #define JSON_HEDLEY_FLAGS #endif #if defined(JSON_HEDLEY_FLAGS_CAST) #undef JSON_HEDLEY_FLAGS_CAST #endif #if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) # define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("warning(disable:188)") \ ((T) (expr)); \ JSON_HEDLEY_DIAGNOSTIC_POP \ })) #else # define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) #endif #if defined(JSON_HEDLEY_EMPTY_BASES) #undef JSON_HEDLEY_EMPTY_BASES #endif #if \ (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) #else #define JSON_HEDLEY_EMPTY_BASES #endif /* Remaining macros are deprecated. */ #if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK #endif #if defined(__clang__) #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) #else #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE #endif #define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) #if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE #endif #define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) #if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) #undef JSON_HEDLEY_CLANG_HAS_BUILTIN #endif #define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) #if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) #undef JSON_HEDLEY_CLANG_HAS_FEATURE #endif #define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) #if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) #undef JSON_HEDLEY_CLANG_HAS_EXTENSION #endif #define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) #if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE #endif #define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) #if defined(JSON_HEDLEY_CLANG_HAS_WARNING) #undef JSON_HEDLEY_CLANG_HAS_WARNING #endif #define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) #endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ // #include #include // #include namespace nlohmann { namespace detail { template struct make_void { using type = void; }; template using void_t = typename make_void::type; } // namespace detail } // namespace nlohmann // https://en.cppreference.com/w/cpp/experimental/is_detected namespace nlohmann { namespace detail { struct nonesuch { nonesuch() = delete; ~nonesuch() = delete; nonesuch(nonesuch const&) = delete; nonesuch(nonesuch const&&) = delete; void operator=(nonesuch const&) = delete; void operator=(nonesuch&&) = delete; }; template class Op, class... Args> struct detector { using value_t = std::false_type; using type = Default; }; template class Op, class... Args> struct detector>, Op, Args...> { using value_t = std::true_type; using type = Op; }; template class Op, class... Args> using is_detected = typename detector::value_t; template class Op, class... Args> struct is_detected_lazy : is_detected { }; template class Op, class... Args> using detected_t = typename detector::type; template class Op, class... Args> using detected_or = detector; template class Op, class... Args> using detected_or_t = typename detected_or::type; template class Op, class... Args> using is_detected_exact = std::is_same>; template class Op, class... Args> using is_detected_convertible = std::is_convertible, To>; } // namespace detail } // namespace nlohmann // This file contains all internal macro definitions // You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) #if defined(__clang__) #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" #endif #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" #endif #endif #endif // C++ language standard detection // if the user manually specified the used c++ version this is skipped #if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) #define JSON_HAS_CPP_20 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define JSON_HAS_CPP_14 #endif // the cpp 11 flag is always specified because it is the minimal required version #define JSON_HAS_CPP_11 #endif // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" #endif // allow to disable exceptions #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception #define JSON_TRY try #define JSON_CATCH(exception) catch(exception) #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include #define JSON_THROW(exception) std::abort() #define JSON_TRY if(true) #define JSON_CATCH(exception) if(false) #define JSON_INTERNAL_CATCH(exception) if(false) #endif // override exception macros #if defined(JSON_THROW_USER) #undef JSON_THROW #define JSON_THROW JSON_THROW_USER #endif #if defined(JSON_TRY_USER) #undef JSON_TRY #define JSON_TRY JSON_TRY_USER #endif #if defined(JSON_CATCH_USER) #undef JSON_CATCH #define JSON_CATCH JSON_CATCH_USER #undef JSON_INTERNAL_CATCH #define JSON_INTERNAL_CATCH JSON_CATCH_USER #endif #if defined(JSON_INTERNAL_CATCH_USER) #undef JSON_INTERNAL_CATCH #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif // allow to override assert #if !defined(JSON_ASSERT) #include // assert #define JSON_ASSERT(x) assert(x) #endif // allow to access some private functions (needed by the test suite) #if defined(JSON_TESTS_PRIVATE) #define JSON_PRIVATE_UNLESS_TESTED public #else #define JSON_PRIVATE_UNLESS_TESTED private #endif /*! @brief macro to briefly define a mapping between an enum and JSON @def NLOHMANN_JSON_SERIALIZE_ENUM @since version 3.4.0 */ #define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ template \ inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ { \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [e](const std::pair& ej_pair) -> bool \ { \ return ej_pair.first == e; \ }); \ j = ((it != std::end(m)) ? it : std::begin(m))->second; \ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [&j](const std::pair& ej_pair) -> bool \ { \ return ej_pair.second == j; \ }); \ e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. #define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ template class ObjectType, \ template class ArrayType, \ class StringType, class BooleanType, class NumberIntegerType, \ class NumberUnsignedType, class NumberFloatType, \ template class AllocatorType, \ template class JSONSerializer, \ class BinaryType> #define NLOHMANN_BASIC_JSON_TPL \ basic_json // Macros to simplify conversion from/to types #define NLOHMANN_JSON_EXPAND( x ) x #define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME #define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ NLOHMANN_JSON_PASTE64, \ NLOHMANN_JSON_PASTE63, \ NLOHMANN_JSON_PASTE62, \ NLOHMANN_JSON_PASTE61, \ NLOHMANN_JSON_PASTE60, \ NLOHMANN_JSON_PASTE59, \ NLOHMANN_JSON_PASTE58, \ NLOHMANN_JSON_PASTE57, \ NLOHMANN_JSON_PASTE56, \ NLOHMANN_JSON_PASTE55, \ NLOHMANN_JSON_PASTE54, \ NLOHMANN_JSON_PASTE53, \ NLOHMANN_JSON_PASTE52, \ NLOHMANN_JSON_PASTE51, \ NLOHMANN_JSON_PASTE50, \ NLOHMANN_JSON_PASTE49, \ NLOHMANN_JSON_PASTE48, \ NLOHMANN_JSON_PASTE47, \ NLOHMANN_JSON_PASTE46, \ NLOHMANN_JSON_PASTE45, \ NLOHMANN_JSON_PASTE44, \ NLOHMANN_JSON_PASTE43, \ NLOHMANN_JSON_PASTE42, \ NLOHMANN_JSON_PASTE41, \ NLOHMANN_JSON_PASTE40, \ NLOHMANN_JSON_PASTE39, \ NLOHMANN_JSON_PASTE38, \ NLOHMANN_JSON_PASTE37, \ NLOHMANN_JSON_PASTE36, \ NLOHMANN_JSON_PASTE35, \ NLOHMANN_JSON_PASTE34, \ NLOHMANN_JSON_PASTE33, \ NLOHMANN_JSON_PASTE32, \ NLOHMANN_JSON_PASTE31, \ NLOHMANN_JSON_PASTE30, \ NLOHMANN_JSON_PASTE29, \ NLOHMANN_JSON_PASTE28, \ NLOHMANN_JSON_PASTE27, \ NLOHMANN_JSON_PASTE26, \ NLOHMANN_JSON_PASTE25, \ NLOHMANN_JSON_PASTE24, \ NLOHMANN_JSON_PASTE23, \ NLOHMANN_JSON_PASTE22, \ NLOHMANN_JSON_PASTE21, \ NLOHMANN_JSON_PASTE20, \ NLOHMANN_JSON_PASTE19, \ NLOHMANN_JSON_PASTE18, \ NLOHMANN_JSON_PASTE17, \ NLOHMANN_JSON_PASTE16, \ NLOHMANN_JSON_PASTE15, \ NLOHMANN_JSON_PASTE14, \ NLOHMANN_JSON_PASTE13, \ NLOHMANN_JSON_PASTE12, \ NLOHMANN_JSON_PASTE11, \ NLOHMANN_JSON_PASTE10, \ NLOHMANN_JSON_PASTE9, \ NLOHMANN_JSON_PASTE8, \ NLOHMANN_JSON_PASTE7, \ NLOHMANN_JSON_PASTE6, \ NLOHMANN_JSON_PASTE5, \ NLOHMANN_JSON_PASTE4, \ NLOHMANN_JSON_PASTE3, \ NLOHMANN_JSON_PASTE2, \ NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) #define NLOHMANN_JSON_PASTE2(func, v1) func(v1) #define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) #define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) #define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) #define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) #define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) #define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) #define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) #define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) #define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) #define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) #define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) #define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) #define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) #define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) #define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) #define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) #define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) #define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) #define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) #define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) #define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) #define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) #define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) #define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) #define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) #define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) #define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) #define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) #define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) #define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) #define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) #define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) #define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) #define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) #define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) #define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) #define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) #define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) #define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) #define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) #define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) #define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) #define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) #define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) #define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) #define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) #define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) #define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) #define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) #define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) #define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) #define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) #define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) #define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) #define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) #define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) #define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) #define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) #define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) #define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) #define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) #define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); /*! @brief macro @def NLOHMANN_DEFINE_TYPE_INTRUSIVE @since version 3.9.0 */ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @since version 3.9.0 */ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } // inspired from https://stackoverflow.com/a/26745591 // allows to call any std function as if (e.g. with begin): // using std::begin; begin(x); // // it allows using the detected idiom to retrieve the return type // of such an expression #define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ namespace detail { \ using std::std_name; \ \ template \ using result_of_##std_name = decltype(std_name(std::declval()...)); \ } \ \ namespace detail2 { \ struct std_name##_tag \ { \ }; \ \ template \ std_name##_tag std_name(T&&...); \ \ template \ using result_of_##std_name = decltype(std_name(std::declval()...)); \ \ template \ struct would_call_std_##std_name \ { \ static constexpr auto const value = ::nlohmann::detail:: \ is_detected_exact::value; \ }; \ } /* namespace detail2 */ \ \ template \ struct would_call_std_##std_name : detail2::would_call_std_##std_name \ { \ } #ifndef JSON_USE_IMPLICIT_CONVERSIONS #define JSON_USE_IMPLICIT_CONVERSIONS 1 #endif #if JSON_USE_IMPLICIT_CONVERSIONS #define JSON_EXPLICIT #else #define JSON_EXPLICIT explicit #endif #ifndef JSON_DIAGNOSTICS #define JSON_DIAGNOSTICS 0 #endif namespace nlohmann { namespace detail { /*! @brief replace all occurrences of a substring by another string @param[in,out] s the string to manipulate; changed so that all occurrences of @a f are replaced with @a t @param[in] f the substring to replace with @a t @param[in] t the string to replace @a f @pre The search string @a f must not be empty. **This precondition is enforced with an assertion.** @since version 2.0.0 */ inline void replace_substring(std::string& s, const std::string& f, const std::string& t) { JSON_ASSERT(!f.empty()); for (auto pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and pos = s.find(f, pos + t.size())) // find next occurrence of f {} } /*! * @brief string escaping as described in RFC 6901 (Sect. 4) * @param[in] s string to escape * @return escaped string * * Note the order of escaping "~" to "~0" and "/" to "~1" is important. */ inline std::string escape(std::string s) { replace_substring(s, "~", "~0"); replace_substring(s, "/", "~1"); return s; } /*! * @brief string unescaping as described in RFC 6901 (Sect. 4) * @param[in] s string to unescape * @return unescaped string * * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ static void unescape(std::string& s) { replace_substring(s, "~1", "/"); replace_substring(s, "~0", "~"); } } // namespace detail } // namespace nlohmann // #include #include // size_t namespace nlohmann { namespace detail { /// struct to capture the start position of the current token struct position_t { /// the total number of characters read std::size_t chars_read_total = 0; /// the number of characters read in the current line std::size_t chars_read_current_line = 0; /// the number of lines read std::size_t lines_read = 0; /// conversion to size_t to preserve SAX interface constexpr operator size_t() const { return chars_read_total; } }; } // namespace detail } // namespace nlohmann // #include namespace nlohmann { namespace detail { //////////////// // exceptions // //////////////// /*! @brief general exception of the @ref basic_json class This class is an extension of `std::exception` objects with a member @a id for exception ids. It is used as the base class for all exceptions thrown by the @ref basic_json class. This class can hence be used as "wildcard" to catch exceptions. Subclasses: - @ref parse_error for exceptions indicating a parse error - @ref invalid_iterator for exceptions indicating errors with iterators - @ref type_error for exceptions indicating executing a member function with a wrong type - @ref out_of_range for exceptions indicating access out of the defined range - @ref other_error for exceptions indicating other library errors @internal @note To have nothrow-copy-constructible exceptions, we internally use `std::runtime_error` which can cope with arbitrary-length error messages. Intermediate strings are built with static functions and then passed to the actual constructor. @endinternal @liveexample{The following code shows how arbitrary library exceptions can be caught.,exception} @since version 3.0.0 */ class exception : public std::exception { public: /// returns the explanatory string const char* what() const noexcept override { return m.what(); } /// the id of the exception const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) protected: JSON_HEDLEY_NON_NULL(3) exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} static std::string name(const std::string& ename, int id_) { return "[json.exception." + ename + "." + std::to_string(id_) + "] "; } template static std::string diagnostics(const BasicJsonType& leaf_element) { #if JSON_DIAGNOSTICS std::vector tokens; for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) { switch (current->m_parent->type()) { case value_t::array: { for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) { if (¤t->m_parent->m_value.array->operator[](i) == current) { tokens.emplace_back(std::to_string(i)); break; } } break; } case value_t::object: { for (const auto& element : *current->m_parent->m_value.object) { if (&element.second == current) { tokens.emplace_back(element.first.c_str()); break; } } break; } case value_t::null: // LCOV_EXCL_LINE case value_t::string: // LCOV_EXCL_LINE case value_t::boolean: // LCOV_EXCL_LINE case value_t::number_integer: // LCOV_EXCL_LINE case value_t::number_unsigned: // LCOV_EXCL_LINE case value_t::number_float: // LCOV_EXCL_LINE case value_t::binary: // LCOV_EXCL_LINE case value_t::discarded: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE break; // LCOV_EXCL_LINE } } if (tokens.empty()) { return ""; } return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, [](const std::string & a, const std::string & b) { return a + "/" + detail::escape(b); }) + ") "; #else static_cast(leaf_element); return ""; #endif } private: /// an exception object as storage for error messages std::runtime_error m; }; /*! @brief exception indicating a parse error This exception is thrown by the library when a parse error occurs. Parse errors can occur during the deserialization of JSON text, CBOR, MessagePack, as well as when using JSON Patch. Member @a byte holds the byte index of the last read character in the input file. Exceptions have ids 1xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. @note For an input with n bytes, 1 is the index of the first character and n+1 is the index of the terminating null byte or the end of file. This also holds true when reading a byte vector (CBOR or MessagePack). @liveexample{The following code shows how a `parse_error` exception can be caught.,parse_error} @sa - @ref exception for the base class of the library exceptions @sa - @ref invalid_iterator for exceptions indicating errors with iterators @sa - @ref type_error for exceptions indicating executing a member function with a wrong type @sa - @ref out_of_range for exceptions indicating access out of the defined range @sa - @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class parse_error : public exception { public: /*! @brief create a parse error exception @param[in] id_ the id of the exception @param[in] pos the position where the error occurred (or with chars_read_total=0 if the position cannot be determined) @param[in] what_arg the explanatory string @return parse_error object */ template static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) { std::string w = exception::name("parse_error", id_) + "parse error" + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; return parse_error(id_, pos.chars_read_total, w.c_str()); } template static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) { std::string w = exception::name("parse_error", id_) + "parse error" + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + ": " + exception::diagnostics(context) + what_arg; return parse_error(id_, byte_, w.c_str()); } /*! @brief byte index of the parse error The byte index of the last read character in the input file. @note For an input with n bytes, 1 is the index of the first character and n+1 is the index of the terminating null byte or the end of file. This also holds true when reading a byte vector (CBOR or MessagePack). */ const std::size_t byte; private: parse_error(int id_, std::size_t byte_, const char* what_arg) : exception(id_, what_arg), byte(byte_) {} static std::string position_string(const position_t& pos) { return " at line " + std::to_string(pos.lines_read + 1) + ", column " + std::to_string(pos.chars_read_current_line); } }; /*! @brief exception indicating errors with iterators This exception is thrown if iterators passed to a library function do not match the expected semantics. Exceptions have ids 2xx. name / id | example message | description ----------------------------------- | --------------- | ------------------------- json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). @liveexample{The following code shows how an `invalid_iterator` exception can be caught.,invalid_iterator} @sa - @ref exception for the base class of the library exceptions @sa - @ref parse_error for exceptions indicating a parse error @sa - @ref type_error for exceptions indicating executing a member function with a wrong type @sa - @ref out_of_range for exceptions indicating access out of the defined range @sa - @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class invalid_iterator : public exception { public: template static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) { std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; return invalid_iterator(id_, w.c_str()); } private: JSON_HEDLEY_NON_NULL(3) invalid_iterator(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating executing a member function with a wrong type This exception is thrown in case of a type error; that is, a library function is executed on a JSON value whose type does not match the expected semantics. Exceptions have ids 3xx. name / id | example message | description ----------------------------- | --------------- | ------------------------- json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | @liveexample{The following code shows how a `type_error` exception can be caught.,type_error} @sa - @ref exception for the base class of the library exceptions @sa - @ref parse_error for exceptions indicating a parse error @sa - @ref invalid_iterator for exceptions indicating errors with iterators @sa - @ref out_of_range for exceptions indicating access out of the defined range @sa - @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class type_error : public exception { public: template static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) { std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; return type_error(id_, w.c_str()); } private: JSON_HEDLEY_NON_NULL(3) type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating access out of the defined range This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance in case of array indices or nonexisting object keys. Exceptions have ids 4xx. name / id | example message | description ------------------------------- | --------------- | ------------------------- json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | @liveexample{The following code shows how an `out_of_range` exception can be caught.,out_of_range} @sa - @ref exception for the base class of the library exceptions @sa - @ref parse_error for exceptions indicating a parse error @sa - @ref invalid_iterator for exceptions indicating errors with iterators @sa - @ref type_error for exceptions indicating executing a member function with a wrong type @sa - @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class out_of_range : public exception { public: template static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) { std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; return out_of_range(id_, w.c_str()); } private: JSON_HEDLEY_NON_NULL(3) out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating other library errors This exception is thrown in case of errors that cannot be classified with the other exception types. Exceptions have ids 5xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. @sa - @ref exception for the base class of the library exceptions @sa - @ref parse_error for exceptions indicating a parse error @sa - @ref invalid_iterator for exceptions indicating errors with iterators @sa - @ref type_error for exceptions indicating executing a member function with a wrong type @sa - @ref out_of_range for exceptions indicating access out of the defined range @liveexample{The following code shows how an `other_error` exception can be caught.,other_error} @since version 3.0.0 */ class other_error : public exception { public: template static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) { std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; return other_error(id_, w.c_str()); } private: JSON_HEDLEY_NON_NULL(3) other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; } // namespace detail } // namespace nlohmann // #include // #include #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type #include // index_sequence, make_index_sequence, index_sequence_for // #include namespace nlohmann { namespace detail { template using uncvref_t = typename std::remove_cv::type>::type; #ifdef JSON_HAS_CPP_14 // the following utilities are natively available in C++14 using std::enable_if_t; using std::index_sequence; using std::make_index_sequence; using std::index_sequence_for; #else // alias templates to reduce boilerplate template using enable_if_t = typename std::enable_if::type; // The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h // which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. //// START OF CODE FROM GOOGLE ABSEIL // integer_sequence // // Class template representing a compile-time integer sequence. An instantiation // of `integer_sequence` has a sequence of integers encoded in its // type through its template arguments (which is a common need when // working with C++11 variadic templates). `absl::integer_sequence` is designed // to be a drop-in replacement for C++14's `std::integer_sequence`. // // Example: // // template< class T, T... Ints > // void user_function(integer_sequence); // // int main() // { // // user_function's `T` will be deduced to `int` and `Ints...` // // will be deduced to `0, 1, 2, 3, 4`. // user_function(make_integer_sequence()); // } template struct integer_sequence { using value_type = T; static constexpr std::size_t size() noexcept { return sizeof...(Ints); } }; // index_sequence // // A helper template for an `integer_sequence` of `size_t`, // `absl::index_sequence` is designed to be a drop-in replacement for C++14's // `std::index_sequence`. template using index_sequence = integer_sequence; namespace utility_internal { template struct Extend; // Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. template struct Extend, SeqSize, 0> { using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; }; template struct Extend, SeqSize, 1> { using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; }; // Recursion helper for 'make_integer_sequence'. // 'Gen::type' is an alias for 'integer_sequence'. template struct Gen { using type = typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; }; template struct Gen { using type = integer_sequence; }; } // namespace utility_internal // Compile-time sequences of integers // make_integer_sequence // // This template alias is equivalent to // `integer_sequence`, and is designed to be a drop-in // replacement for C++14's `std::make_integer_sequence`. template using make_integer_sequence = typename utility_internal::Gen::type; // make_index_sequence // // This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, // and is designed to be a drop-in replacement for C++14's // `std::make_index_sequence`. template using make_index_sequence = make_integer_sequence; // index_sequence_for // // Converts a typename pack into an index sequence of the same length, and // is designed to be a drop-in replacement for C++14's // `std::index_sequence_for()` template using index_sequence_for = make_index_sequence; //// END OF CODE FROM GOOGLE ABSEIL #endif // dispatch utility (taken from ranges-v3) template struct priority_tag : priority_tag < N - 1 > {}; template<> struct priority_tag<0> {}; // taken from ranges-v3 template struct static_const { static constexpr T value{}; }; template constexpr T static_const::value; } // namespace detail } // namespace nlohmann // #include namespace nlohmann { namespace detail { // dispatching helper struct template struct identity_tag {}; } // namespace detail } // namespace nlohmann // #include #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval #include // tuple // #include // #include #include // random_access_iterator_tag // #include // #include namespace nlohmann { namespace detail { template struct iterator_types {}; template struct iterator_types < It, void_t> { using difference_type = typename It::difference_type; using value_type = typename It::value_type; using pointer = typename It::pointer; using reference = typename It::reference; using iterator_category = typename It::iterator_category; }; // This is required as some compilers implement std::iterator_traits in a way that // doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. template struct iterator_traits { }; template struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> : iterator_types { }; template struct iterator_traits::value>> { using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = ptrdiff_t; using pointer = T*; using reference = T&; }; } // namespace detail } // namespace nlohmann // #include // #include namespace nlohmann { NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); } // namespace nlohmann // #include // #include namespace nlohmann { NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); } // namespace nlohmann // #include // #include // #include #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ #include // int64_t, uint64_t #include // map #include // allocator #include // string #include // vector /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @since version 1.0.0 */ namespace nlohmann { /*! @brief default JSONSerializer template argument This serializer ignores the template arguments and uses ADL ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) for serialization. */ template struct adl_serializer; template class ObjectType = std::map, template class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, class NumberIntegerType = std::int64_t, class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, template class AllocatorType = std::allocator, template class JSONSerializer = adl_serializer, class BinaryType = std::vector> class basic_json; /*! @brief JSON Pointer A JSON pointer defines a string syntax for identifying a specific value within a JSON document. It can be used with functions `at` and `operator[]`. Furthermore, JSON pointers are the base for JSON patches. @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) @since version 2.0.0 */ template class json_pointer; /*! @brief default JSON class This type is the default specialization of the @ref basic_json class which uses the standard template types. @since version 1.0.0 */ using json = basic_json<>; template struct ordered_map; /*! @brief ordered JSON class This type preserves the insertion order of object keys. @since version 3.9.0 */ using ordered_json = basic_json; } // namespace nlohmann #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ namespace nlohmann { /*! @brief detail namespace with internal helper functions This namespace collects functions that should not be exposed, implementations of some @ref basic_json methods, and meta-programming helpers. @since version 2.1.0 */ namespace detail { ///////////// // helpers // ///////////// // Note to maintainers: // // Every trait in this file expects a non CV-qualified type. // The only exceptions are in the 'aliases for detected' section // (i.e. those of the form: decltype(T::member_function(std::declval()))) // // In this case, T has to be properly CV-qualified to constraint the function arguments // (e.g. to_json(BasicJsonType&, const T&)) template struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; ////////////////////// // json_ref helpers // ////////////////////// template class json_ref; template struct is_json_ref : std::false_type {}; template struct is_json_ref> : std::true_type {}; ////////////////////////// // aliases for detected // ////////////////////////// template using mapped_type_t = typename T::mapped_type; template using key_type_t = typename T::key_type; template using value_type_t = typename T::value_type; template using difference_type_t = typename T::difference_type; template using pointer_t = typename T::pointer; template using reference_t = typename T::reference; template using iterator_category_t = typename T::iterator_category; template using to_json_function = decltype(T::to_json(std::declval()...)); template using from_json_function = decltype(T::from_json(std::declval()...)); template using get_template_function = decltype(std::declval().template get()); // trait checking if JSONSerializer::from_json(json const&, udt&) exists template struct has_from_json : std::false_type {}; // trait checking if j.get is valid // use this trait instead of std::is_constructible or std::is_convertible, // both rely on, or make use of implicit conversions, and thus fail when T // has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) template struct is_getable { static constexpr bool value = is_detected::value; }; template struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> { using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = is_detected_exact::value; }; // This trait checks if JSONSerializer::from_json(json const&) exists // this overload is used for non-default-constructible user-defined-types template struct has_non_default_from_json : std::false_type {}; template struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> { using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = is_detected_exact::value; }; // This trait checks if BasicJsonType::json_serializer::to_json exists // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. template struct has_to_json : std::false_type {}; template struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> { using serializer = typename BasicJsonType::template json_serializer; static constexpr bool value = is_detected_exact::value; }; /////////////////// // is_ functions // /////////////////// // https://en.cppreference.com/w/cpp/types/conjunction template struct conjunction : std::true_type { }; template struct conjunction : B1 { }; template struct conjunction : std::conditional, B1>::type {}; // https://en.cppreference.com/w/cpp/types/negation template struct negation : std::integral_constant < bool, !B::value > { }; // Reimplementation of is_constructible and is_default_constructible, due to them being broken for // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). // This causes compile errors in e.g. clang 3.5 or gcc 4.9. template struct is_default_constructible : std::is_default_constructible {}; template struct is_default_constructible> : conjunction, is_default_constructible> {}; template struct is_default_constructible> : conjunction, is_default_constructible> {}; template struct is_default_constructible> : conjunction...> {}; template struct is_default_constructible> : conjunction...> {}; template struct is_constructible : std::is_constructible {}; template struct is_constructible> : is_default_constructible> {}; template struct is_constructible> : is_default_constructible> {}; template struct is_constructible> : is_default_constructible> {}; template struct is_constructible> : is_default_constructible> {}; template struct is_iterator_traits : std::false_type {}; template struct is_iterator_traits> { private: using traits = iterator_traits; public: static constexpr auto value = is_detected::value && is_detected::value && is_detected::value && is_detected::value && is_detected::value; }; template struct is_range { private: using t_ref = typename std::add_lvalue_reference::type; using iterator = detected_t; using sentinel = detected_t; // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator // and https://en.cppreference.com/w/cpp/iterator/sentinel_for // but reimplementing these would be too much work, as a lot of other concepts are used underneath static constexpr auto is_iterator_begin = is_iterator_traits>::value; public: static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; }; template using iterator_t = enable_if_t::value, result_of_begin())>>; template using range_value_t = value_type_t>>; // The following implementation of is_complete_type is taken from // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ // and is written by Xiang Fan who agreed to using it in this library. template struct is_complete_type : std::false_type {}; template struct is_complete_type : std::true_type {}; template struct is_compatible_object_type_impl : std::false_type {}; template struct is_compatible_object_type_impl < BasicJsonType, CompatibleObjectType, enable_if_t < is_detected::value&& is_detected::value >> { using object_t = typename BasicJsonType::object_t; // macOS's is_constructible does not play well with nonesuch... static constexpr bool value = is_constructible::value && is_constructible::value; }; template struct is_compatible_object_type : is_compatible_object_type_impl {}; template struct is_constructible_object_type_impl : std::false_type {}; template struct is_constructible_object_type_impl < BasicJsonType, ConstructibleObjectType, enable_if_t < is_detected::value&& is_detected::value >> { using object_t = typename BasicJsonType::object_t; static constexpr bool value = (is_default_constructible::value && (std::is_move_assignable::value || std::is_copy_assignable::value) && (is_constructible::value && std::is_same < typename object_t::mapped_type, typename ConstructibleObjectType::mapped_type >::value)) || (has_from_json::value || has_non_default_from_json < BasicJsonType, typename ConstructibleObjectType::mapped_type >::value); }; template struct is_constructible_object_type : is_constructible_object_type_impl {}; template struct is_compatible_string_type { static constexpr auto value = is_constructible::value; }; template struct is_constructible_string_type { static constexpr auto value = is_constructible::value; }; template struct is_compatible_array_type_impl : std::false_type {}; template struct is_compatible_array_type_impl < BasicJsonType, CompatibleArrayType, enable_if_t < is_detected::value&& is_iterator_traits>>::value&& // special case for types like std::filesystem::path whose iterator's value_type are themselves // c.f. https://github.com/nlohmann/json/pull/3073 !std::is_same>::value >> { static constexpr bool value = is_constructible>::value; }; template struct is_compatible_array_type : is_compatible_array_type_impl {}; template struct is_constructible_array_type_impl : std::false_type {}; template struct is_constructible_array_type_impl < BasicJsonType, ConstructibleArrayType, enable_if_t::value >> : std::true_type {}; template struct is_constructible_array_type_impl < BasicJsonType, ConstructibleArrayType, enable_if_t < !std::is_same::value&& !is_compatible_string_type::value&& is_default_constructible::value&& (std::is_move_assignable::value || std::is_copy_assignable::value)&& is_detected::value&& is_iterator_traits>>::value&& is_detected::value&& // special case for types like std::filesystem::path whose iterator's value_type are themselves // c.f. https://github.com/nlohmann/json/pull/3073 !std::is_same>::value&& is_complete_type < detected_t>::value >> { using value_type = range_value_t; static constexpr bool value = std::is_same::value || has_from_json::value || has_non_default_from_json < BasicJsonType, value_type >::value; }; template struct is_constructible_array_type : is_constructible_array_type_impl {}; template struct is_compatible_integer_type_impl : std::false_type {}; template struct is_compatible_integer_type_impl < RealIntegerType, CompatibleNumberIntegerType, enable_if_t < std::is_integral::value&& std::is_integral::value&& !std::is_same::value >> { // is there an assert somewhere on overflows? using RealLimits = std::numeric_limits; using CompatibleLimits = std::numeric_limits; static constexpr auto value = is_constructible::value && CompatibleLimits::is_integer && RealLimits::is_signed == CompatibleLimits::is_signed; }; template struct is_compatible_integer_type : is_compatible_integer_type_impl {}; template struct is_compatible_type_impl: std::false_type {}; template struct is_compatible_type_impl < BasicJsonType, CompatibleType, enable_if_t::value >> { static constexpr bool value = has_to_json::value; }; template struct is_compatible_type : is_compatible_type_impl {}; template struct is_constructible_tuple : std::false_type {}; template struct is_constructible_tuple> : conjunction...> {}; // a naive helper to check if a type is an ordered_map (exploits the fact that // ordered_map inherits capacity() from std::vector) template struct is_ordered_map { using one = char; struct two { char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) }; template static one test( decltype(&C::capacity) ) ; template static two test(...); enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) }; // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > T conditional_static_cast(U value) { return static_cast(value); } template::value, int> = 0> T conditional_static_cast(U value) { return value; } } // namespace detail } // namespace nlohmann // #include #ifdef JSON_HAS_CPP_17 #include #endif namespace nlohmann { namespace detail { template void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); } n = nullptr; } // overloads for basic_json template parameters template < typename BasicJsonType, typename ArithmeticType, enable_if_t < std::is_arithmetic::value&& !std::is_same::value, int > = 0 > void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) { case value_t::number_unsigned: { val = static_cast(*j.template get_ptr()); break; } case value_t::number_integer: { val = static_cast(*j.template get_ptr()); break; } case value_t::number_float: { val = static_cast(*j.template get_ptr()); break; } case value_t::null: case value_t::object: case value_t::array: case value_t::string: case value_t::boolean: case value_t::binary: case value_t::discarded: default: JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); } } template void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); } b = *j.template get_ptr(); } template void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } s = *j.template get_ptr(); } template < typename BasicJsonType, typename ConstructibleStringType, enable_if_t < is_constructible_string_type::value&& !std::is_same::value, int > = 0 > void from_json(const BasicJsonType& j, ConstructibleStringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } s = *j.template get_ptr(); } template void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) { get_arithmetic_value(j, val); } template void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) { get_arithmetic_value(j, val); } template void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) { get_arithmetic_value(j, val); } template::value, int> = 0> void from_json(const BasicJsonType& j, EnumType& e) { typename std::underlying_type::type val; get_arithmetic_value(j, val); e = static_cast(val); } // forward_list doesn't have an insert method template::value, int> = 0> void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } l.clear(); std::transform(j.rbegin(), j.rend(), std::front_inserter(l), [](const BasicJsonType & i) { return i.template get(); }); } // valarray doesn't have an insert method template::value, int> = 0> void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } l.resize(j.size()); std::transform(j.begin(), j.end(), std::begin(l), [](const BasicJsonType & elem) { return elem.template get(); }); } template auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) -> decltype(j.template get(), void()) { for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); } } template void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) { arr = *j.template get_ptr(); } template auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); } } template::value, int> = 0> auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) -> decltype( arr.reserve(std::declval()), j.template get(), void()) { using std::end; ConstructibleArrayType ret; ret.reserve(j.size()); std::transform(j.begin(), j.end(), std::inserter(ret, end(ret)), [](const BasicJsonType & i) { // get() returns *this, this won't call a from_json // method when value_type is BasicJsonType return i.template get(); }); arr = std::move(ret); } template::value, int> = 0> void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<0> /*unused*/) { using std::end; ConstructibleArrayType ret; std::transform( j.begin(), j.end(), std::inserter(ret, end(ret)), [](const BasicJsonType & i) { // get() returns *this, this won't call a from_json // method when value_type is BasicJsonType return i.template get(); }); arr = std::move(ret); } template < typename BasicJsonType, typename ConstructibleArrayType, enable_if_t < is_constructible_array_type::value&& !is_constructible_object_type::value&& !is_constructible_string_type::value&& !std::is_same::value&& !is_basic_json::value, int > = 0 > auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), j.template get(), void()) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } from_json_array_impl(j, arr, priority_tag<3> {}); } template < typename BasicJsonType, typename T, std::size_t... Idx > std::array from_json_inplace_array_impl(BasicJsonType&& j, identity_tag> /*unused*/, index_sequence /*unused*/) { return { { std::forward(j).at(Idx).template get()... } }; } template < typename BasicJsonType, typename T, std::size_t N > auto from_json(BasicJsonType&& j, identity_tag> tag) -> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); } template void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); } bin = *j.template get_ptr(); } template::value, int> = 0> void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); } ConstructibleObjectType ret; const auto* inner_object = j.template get_ptr(); using value_type = typename ConstructibleObjectType::value_type; std::transform( inner_object->begin(), inner_object->end(), std::inserter(ret, ret.begin()), [](typename BasicJsonType::object_t::value_type const & p) { return value_type(p.first, p.second.template get()); }); obj = std::move(ret); } // overload for arithmetic types, not chosen for basic_json template arguments // (BooleanType, etc..); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not // an arithmetic type? template < typename BasicJsonType, typename ArithmeticType, enable_if_t < std::is_arithmetic::value&& !std::is_same::value&& !std::is_same::value&& !std::is_same::value&& !std::is_same::value, int > = 0 > void from_json(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) { case value_t::number_unsigned: { val = static_cast(*j.template get_ptr()); break; } case value_t::number_integer: { val = static_cast(*j.template get_ptr()); break; } case value_t::number_float: { val = static_cast(*j.template get_ptr()); break; } case value_t::boolean: { val = static_cast(*j.template get_ptr()); break; } case value_t::null: case value_t::object: case value_t::array: case value_t::string: case value_t::binary: case value_t::discarded: default: JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); } } template std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) { return std::make_tuple(std::forward(j).at(Idx).template get()...); } template < typename BasicJsonType, class A1, class A2 > std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) { return {std::forward(j).at(0).template get(), std::forward(j).at(1).template get()}; } template void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) { p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } template std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) { return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } template void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) { t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } template auto from_json(BasicJsonType&& j, TupleRelated&& t) -> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, typename = enable_if_t < !std::is_constructible < typename BasicJsonType::string_t, Key >::value >> void from_json(const BasicJsonType& j, std::map& m) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); } m.emplace(p.at(0).template get(), p.at(1).template get()); } } template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, typename = enable_if_t < !std::is_constructible < typename BasicJsonType::string_t, Key >::value >> void from_json(const BasicJsonType& j, std::unordered_map& m) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); } m.emplace(p.at(0).template get(), p.at(1).template get()); } } #ifdef JSON_HAS_CPP_17 template void from_json(const BasicJsonType& j, std::filesystem::path& p) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } p = *j.template get_ptr(); } #endif struct from_json_fn { template auto operator()(const BasicJsonType& j, T&& val) const noexcept(noexcept(from_json(j, std::forward(val)))) -> decltype(from_json(j, std::forward(val))) { return from_json(j, std::forward(val)); } }; } // namespace detail /// namespace to hold default `from_json` function /// to see why this is required: /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) { constexpr const auto& from_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) } // namespace } // namespace nlohmann // #include #include // copy #include // begin, end #include // string #include // tuple, get #include // is_same, is_constructible, is_floating_point, is_enum, underlying_type #include // move, forward, declval, pair #include // valarray #include // vector // #include // #include #include // size_t #include // input_iterator_tag #include // string, to_string #include // tuple_size, get, tuple_element #include // move // #include // #include namespace nlohmann { namespace detail { template void int_to_string( string_type& target, std::size_t value ) { // For ADL using std::to_string; target = to_string(value); } template class iteration_proxy_value { public: using difference_type = std::ptrdiff_t; using value_type = iteration_proxy_value; using pointer = value_type * ; using reference = value_type & ; using iterator_category = std::input_iterator_tag; using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; private: /// the iterator IteratorType anchor; /// an index for arrays (used to create key names) std::size_t array_index = 0; /// last stringified array index mutable std::size_t array_index_last = 0; /// a string representation of the array index mutable string_type array_index_str = "0"; /// an empty string (to return a reference for primitive values) const string_type empty_str{}; public: explicit iteration_proxy_value(IteratorType it) noexcept : anchor(std::move(it)) {} /// dereference operator (needed for range-based for) iteration_proxy_value& operator*() { return *this; } /// increment operator (needed for range-based for) iteration_proxy_value& operator++() { ++anchor; ++array_index; return *this; } /// equality operator (needed for InputIterator) bool operator==(const iteration_proxy_value& o) const { return anchor == o.anchor; } /// inequality operator (needed for range-based for) bool operator!=(const iteration_proxy_value& o) const { return anchor != o.anchor; } /// return key of the iterator const string_type& key() const { JSON_ASSERT(anchor.m_object != nullptr); switch (anchor.m_object->type()) { // use integer array index as key case value_t::array: { if (array_index != array_index_last) { int_to_string( array_index_str, array_index ); array_index_last = array_index; } return array_index_str; } // use key from the object case value_t::object: return anchor.key(); // use an empty key for all primitive types case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: return empty_str; } } /// return value of the iterator typename IteratorType::reference value() const { return anchor.value(); } }; /// proxy class for the items() function template class iteration_proxy { private: /// the container to iterate typename IteratorType::reference container; public: /// construct iteration proxy from a container explicit iteration_proxy(typename IteratorType::reference cont) noexcept : container(cont) {} /// return iterator begin (needed for range-based for) iteration_proxy_value begin() noexcept { return iteration_proxy_value(container.begin()); } /// return iterator end (needed for range-based for) iteration_proxy_value end() noexcept { return iteration_proxy_value(container.end()); } }; // Structured Bindings Support // For further reference see https://blog.tartanllama.xyz/structured-bindings/ // And see https://github.com/nlohmann/json/pull/1391 template = 0> auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) { return i.key(); } // Structured Bindings Support // For further reference see https://blog.tartanllama.xyz/structured-bindings/ // And see https://github.com/nlohmann/json/pull/1391 template = 0> auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) { return i.value(); } } // namespace detail } // namespace nlohmann // The Addition to the STD Namespace is required to add // Structured Bindings Support to the iteration_proxy_value class // For further reference see https://blog.tartanllama.xyz/structured-bindings/ // And see https://github.com/nlohmann/json/pull/1391 namespace std { #if defined(__clang__) // Fix: https://github.com/nlohmann/json/issues/1401 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmismatched-tags" #endif template class tuple_size<::nlohmann::detail::iteration_proxy_value> : public std::integral_constant {}; template class tuple_element> { public: using type = decltype( get(std::declval < ::nlohmann::detail::iteration_proxy_value> ())); }; #if defined(__clang__) #pragma clang diagnostic pop #endif } // namespace std // #include // #include // #include #ifdef JSON_HAS_CPP_17 #include #endif namespace nlohmann { namespace detail { ////////////////// // constructors // ////////////////// /* * Note all external_constructor<>::construct functions need to call * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an * allocated value (e.g., a string). See bug issue * https://github.com/nlohmann/json/issues/2865 for more information. */ template struct external_constructor; template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept { j.m_value.destroy(j.m_type); j.m_type = value_t::boolean; j.m_value = b; j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) { j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value = s; j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) { j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value = std::move(s); j.assert_invariant(); } template < typename BasicJsonType, typename CompatibleStringType, enable_if_t < !std::is_same::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleStringType& str) { j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value.string = j.template create(str); j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) { j.m_value.destroy(j.m_type); j.m_type = value_t::binary; j.m_value = typename BasicJsonType::binary_t(b); j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) { j.m_value.destroy(j.m_type); j.m_type = value_t::binary; j.m_value = typename BasicJsonType::binary_t(std::move(b)); j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept { j.m_value.destroy(j.m_type); j.m_type = value_t::number_float; j.m_value = val; j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept { j.m_value.destroy(j.m_type); j.m_type = value_t::number_unsigned; j.m_value = val; j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept { j.m_value.destroy(j.m_type); j.m_type = value_t::number_integer; j.m_value = val; j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) { j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = arr; j.set_parents(); j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = std::move(arr); j.set_parents(); j.assert_invariant(); } template < typename BasicJsonType, typename CompatibleArrayType, enable_if_t < !std::is_same::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleArrayType& arr) { using std::begin; using std::end; j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value.array = j.template create(begin(arr), end(arr)); j.set_parents(); j.assert_invariant(); } template static void construct(BasicJsonType& j, const std::vector& arr) { j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = value_t::array; j.m_value.array->reserve(arr.size()); for (const bool x : arr) { j.m_value.array->push_back(x); j.set_parent(j.m_value.array->back()); } j.assert_invariant(); } template::value, int> = 0> static void construct(BasicJsonType& j, const std::valarray& arr) { j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = value_t::array; j.m_value.array->resize(arr.size()); if (arr.size() > 0) { std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); } j.set_parents(); j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) { j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value = obj; j.set_parents(); j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value = std::move(obj); j.set_parents(); j.assert_invariant(); } template < typename BasicJsonType, typename CompatibleObjectType, enable_if_t < !std::is_same::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; using std::end; j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value.object = j.template create(begin(obj), end(obj)); j.set_parents(); j.assert_invariant(); } }; ///////////// // to_json // ///////////// template::value, int> = 0> void to_json(BasicJsonType& j, T b) noexcept { external_constructor::construct(j, b); } template::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleString& s) { external_constructor::construct(j, s); } template void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) { external_constructor::construct(j, std::move(s)); } template::value, int> = 0> void to_json(BasicJsonType& j, FloatType val) noexcept { external_constructor::construct(j, static_cast(val)); } template::value, int> = 0> void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept { external_constructor::construct(j, static_cast(val)); } template::value, int> = 0> void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept { external_constructor::construct(j, static_cast(val)); } template::value, int> = 0> void to_json(BasicJsonType& j, EnumType e) noexcept { using underlying_type = typename std::underlying_type::type; external_constructor::construct(j, static_cast(e)); } template void to_json(BasicJsonType& j, const std::vector& e) { external_constructor::construct(j, e); } template < typename BasicJsonType, typename CompatibleArrayType, enable_if_t < is_compatible_array_type::value&& !is_compatible_object_type::value&& !is_compatible_string_type::value&& !std::is_same::value&& !is_basic_json::value, int > = 0 > void to_json(BasicJsonType& j, const CompatibleArrayType& arr) { external_constructor::construct(j, arr); } template void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) { external_constructor::construct(j, bin); } template::value, int> = 0> void to_json(BasicJsonType& j, const std::valarray& arr) { external_constructor::construct(j, std::move(arr)); } template void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { external_constructor::construct(j, std::move(arr)); } template < typename BasicJsonType, typename CompatibleObjectType, enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { external_constructor::construct(j, obj); } template void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { external_constructor::construct(j, std::move(obj)); } template < typename BasicJsonType, typename T, std::size_t N, enable_if_t < !std::is_constructible::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) int > = 0 > void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) { external_constructor::construct(j, arr); } template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > void to_json(BasicJsonType& j, const std::pair& p) { j = { p.first, p.second }; } // for https://github.com/nlohmann/json/pull/1134 template>::value, int> = 0> void to_json(BasicJsonType& j, const T& b) { j = { {b.key(), b.value()} }; } template void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) { j = { std::get(t)... }; } template::value, int > = 0> void to_json(BasicJsonType& j, const T& t) { to_json_tuple_impl(j, t, make_index_sequence::value> {}); } #ifdef JSON_HAS_CPP_17 template void to_json(BasicJsonType& j, const std::filesystem::path& p) { j = p.string(); } #endif struct to_json_fn { template auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) -> decltype(to_json(j, std::forward(val)), void()) { return to_json(j, std::forward(val)); } }; } // namespace detail /// namespace to hold default `to_json` function /// to see why this is required: /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) { constexpr const auto& to_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) } // namespace } // namespace nlohmann // #include // #include namespace nlohmann { template struct adl_serializer { /*! @brief convert a JSON value to any value type This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). @note This function is chosen for default-constructible value types. @param[in] j JSON value to read from @param[in,out] val value to write to */ template static auto from_json(BasicJsonType && j, TargetType& val) noexcept( noexcept(::nlohmann::from_json(std::forward(j), val))) -> decltype(::nlohmann::from_json(std::forward(j), val), void()) { ::nlohmann::from_json(std::forward(j), val); } /*! @brief convert a JSON value to any value type This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). @note This function is chosen for value types which are not default-constructible. @param[in] j JSON value to read from @return copy of the JSON value, converted to @a ValueType */ template static auto from_json(BasicJsonType && j) noexcept( noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) { return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); } /*! @brief convert any value type to a JSON value This function is usually called by the constructors of the @ref basic_json class. @param[in,out] j JSON value to write to @param[in] val value to read from */ template static auto to_json(BasicJsonType& j, TargetType && val) noexcept( noexcept(::nlohmann::to_json(j, std::forward(val)))) -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) { ::nlohmann::to_json(j, std::forward(val)); } }; } // namespace nlohmann // #include #include // uint8_t, uint64_t #include // tie #include // move namespace nlohmann { /*! @brief an internal type for a backed binary type This type extends the template parameter @a BinaryType provided to `basic_json` with a subtype used by BSON and MessagePack. This type exists so that the user does not have to specify a type themselves with a specific naming scheme in order to override the binary type. @tparam BinaryType container to store bytes (`std::vector` by default) @since version 3.8.0; changed type of subtypes to std::uint64_t in 3.10.0. */ template class byte_container_with_subtype : public BinaryType { public: /// the type of the underlying container using container_type = BinaryType; /// the type of the subtype using subtype_type = std::uint64_t; byte_container_with_subtype() noexcept(noexcept(container_type())) : container_type() {} byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) : container_type(b) {} byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) : container_type(std::move(b)) {} byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) : container_type(b) , m_subtype(subtype_) , m_has_subtype(true) {} byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) : container_type(std::move(b)) , m_subtype(subtype_) , m_has_subtype(true) {} bool operator==(const byte_container_with_subtype& rhs) const { return std::tie(static_cast(*this), m_subtype, m_has_subtype) == std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); } bool operator!=(const byte_container_with_subtype& rhs) const { return !(rhs == *this); } /*! @brief sets the binary subtype Sets the binary subtype of the value, also flags a binary JSON value as having a subtype, which has implications for serialization. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @sa see @ref subtype() -- return the binary subtype @sa see @ref clear_subtype() -- clears the binary subtype @sa see @ref has_subtype() -- returns whether or not the binary value has a subtype @since version 3.8.0 */ void set_subtype(subtype_type subtype_) noexcept { m_subtype = subtype_; m_has_subtype = true; } /*! @brief return the binary subtype Returns the numerical subtype of the value if it has a subtype. If it does not have a subtype, this function will return subtype_type(-1) as a sentinel value. @return the numerical subtype of the binary value @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @sa see @ref set_subtype() -- sets the binary subtype @sa see @ref clear_subtype() -- clears the binary subtype @sa see @ref has_subtype() -- returns whether or not the binary value has a subtype @since version 3.8.0; fixed return value to properly return subtype_type(-1) as documented in version 3.10.0 */ constexpr subtype_type subtype() const noexcept { return m_has_subtype ? m_subtype : subtype_type(-1); } /*! @brief return whether the value has a subtype @return whether the value has a subtype @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @sa see @ref subtype() -- return the binary subtype @sa see @ref set_subtype() -- sets the binary subtype @sa see @ref clear_subtype() -- clears the binary subtype @since version 3.8.0 */ constexpr bool has_subtype() const noexcept { return m_has_subtype; } /*! @brief clears the binary subtype Clears the binary subtype and flags the value as not having a subtype, which has implications for serialization; for instance MessagePack will prefer the bin family over the ext family. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @sa see @ref subtype() -- return the binary subtype @sa see @ref set_subtype() -- sets the binary subtype @sa see @ref has_subtype() -- returns whether or not the binary value has a subtype @since version 3.8.0 */ void clear_subtype() noexcept { m_subtype = 0; m_has_subtype = false; } private: subtype_type m_subtype = 0; bool m_has_subtype = false; }; } // namespace nlohmann // #include // #include // #include // #include #include // uint8_t #include // size_t #include // hash // #include // #include namespace nlohmann { namespace detail { // boost::hash_combine inline std::size_t combine(std::size_t seed, std::size_t h) noexcept { seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); return seed; } /*! @brief hash a JSON value The hash function tries to rely on std::hash where possible. Furthermore, the type of the JSON value is taken into account to have different hash values for null, 0, 0U, and false, etc. @tparam BasicJsonType basic_json specialization @param j JSON value to hash @return hash value of j */ template std::size_t hash(const BasicJsonType& j) { using string_t = typename BasicJsonType::string_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; const auto type = static_cast(j.type()); switch (j.type()) { case BasicJsonType::value_t::null: case BasicJsonType::value_t::discarded: { return combine(type, 0); } case BasicJsonType::value_t::object: { auto seed = combine(type, j.size()); for (const auto& element : j.items()) { const auto h = std::hash {}(element.key()); seed = combine(seed, h); seed = combine(seed, hash(element.value())); } return seed; } case BasicJsonType::value_t::array: { auto seed = combine(type, j.size()); for (const auto& element : j) { seed = combine(seed, hash(element)); } return seed; } case BasicJsonType::value_t::string: { const auto h = std::hash {}(j.template get_ref()); return combine(type, h); } case BasicJsonType::value_t::boolean: { const auto h = std::hash {}(j.template get()); return combine(type, h); } case BasicJsonType::value_t::number_integer: { const auto h = std::hash {}(j.template get()); return combine(type, h); } case BasicJsonType::value_t::number_unsigned: { const auto h = std::hash {}(j.template get()); return combine(type, h); } case BasicJsonType::value_t::number_float: { const auto h = std::hash {}(j.template get()); return combine(type, h); } case BasicJsonType::value_t::binary: { auto seed = combine(type, j.get_binary().size()); const auto h = std::hash {}(j.get_binary().has_subtype()); seed = combine(seed, h); seed = combine(seed, static_cast(j.get_binary().subtype())); for (const auto byte : j.get_binary()) { seed = combine(seed, std::hash {}(byte)); } return seed; } default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE return 0; // LCOV_EXCL_LINE } } } // namespace detail } // namespace nlohmann // #include #include // generate_n #include // array #include // ldexp #include // size_t #include // uint8_t, uint16_t, uint32_t, uint64_t #include // snprintf #include // memcpy #include // back_inserter #include // numeric_limits #include // char_traits, string #include // make_pair, move #include // vector // #include // #include #include // array #include // size_t #include // strlen #include // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include // shared_ptr, make_shared, addressof #include // accumulate #include // string, char_traits #include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include // pair, declval #ifndef JSON_NO_IO #include // FILE * #include // istream #endif // JSON_NO_IO // #include // #include namespace nlohmann { namespace detail { /// the supported input formats enum class input_format_t { json, cbor, msgpack, ubjson, bson }; //////////////////// // input adapters // //////////////////// #ifndef JSON_NO_IO /*! Input adapter for stdio file access. This adapter read only 1 byte and do not use any buffer. This adapter is a very low level adapter. */ class file_input_adapter { public: using char_type = char; JSON_HEDLEY_NON_NULL(2) explicit file_input_adapter(std::FILE* f) noexcept : m_file(f) {} // make class move-only file_input_adapter(const file_input_adapter&) = delete; file_input_adapter(file_input_adapter&&) noexcept = default; file_input_adapter& operator=(const file_input_adapter&) = delete; file_input_adapter& operator=(file_input_adapter&&) = delete; ~file_input_adapter() = default; std::char_traits::int_type get_character() noexcept { return std::fgetc(m_file); } private: /// the file pointer to read from std::FILE* m_file; }; /*! Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at beginning of input. Does not support changing the underlying std::streambuf in mid-input. Maintains underlying std::istream and std::streambuf to support subsequent use of standard std::istream operations to process any input characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ class input_stream_adapter { public: using char_type = char; ~input_stream_adapter() { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof if (is != nullptr) { is->clear(is->rdstate() & std::ios::eofbit); } } explicit input_stream_adapter(std::istream& i) : is(&i), sb(i.rdbuf()) {} // delete because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&&) = delete; input_stream_adapter(input_stream_adapter&& rhs) noexcept : is(rhs.is), sb(rhs.sb) { rhs.is = nullptr; rhs.sb = nullptr; } // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not // end up as the same value, eg. 0xFFFFFFFF. std::char_traits::int_type get_character() { auto res = sb->sbumpc(); // set eof manually, as we don't use the istream interface. if (JSON_HEDLEY_UNLIKELY(res == std::char_traits::eof())) { is->clear(is->rdstate() | std::ios::eofbit); } return res; } private: /// the associated input stream std::istream* is = nullptr; std::streambuf* sb = nullptr; }; #endif // JSON_NO_IO // General-purpose iterator-based adapter. It might not be as fast as // theoretically possible for some containers, but it is extremely versatile. template class iterator_input_adapter { public: using char_type = typename std::iterator_traits::value_type; iterator_input_adapter(IteratorType first, IteratorType last) : current(std::move(first)), end(std::move(last)) {} typename std::char_traits::int_type get_character() { if (JSON_HEDLEY_LIKELY(current != end)) { auto result = std::char_traits::to_int_type(*current); std::advance(current, 1); return result; } return std::char_traits::eof(); } private: IteratorType current; IteratorType end; template friend struct wide_string_input_helper; bool empty() const { return current == end; } }; template struct wide_string_input_helper; template struct wide_string_input_helper { // UTF-32 static void fill_buffer(BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; } else { // get the current character const auto wc = input.get_character(); // UTF-32 to UTF-8 encoding if (wc < 0x80) { utf8_bytes[0] = static_cast::int_type>(wc); utf8_bytes_filled = 1; } else if (wc <= 0x7FF) { utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 2; } else if (wc <= 0xFFFF) { utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 3; } else if (wc <= 0x10FFFF) { utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 4; } else { // unknown character utf8_bytes[0] = static_cast::int_type>(wc); utf8_bytes_filled = 1; } } } }; template struct wide_string_input_helper { // UTF-16 static void fill_buffer(BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; } else { // get the current character const auto wc = input.get_character(); // UTF-16 to UTF-8 encoding if (wc < 0x80) { utf8_bytes[0] = static_cast::int_type>(wc); utf8_bytes_filled = 1; } else if (wc <= 0x7FF) { utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 2; } else if (0xD800 > wc || wc >= 0xE000) { utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 3; } else { if (JSON_HEDLEY_UNLIKELY(!input.empty())) { const auto wc2 = static_cast(input.get_character()); const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); utf8_bytes_filled = 4; } else { utf8_bytes[0] = static_cast::int_type>(wc); utf8_bytes_filled = 1; } } } } }; // Wraps another input apdater to convert wide character types into individual bytes. template class wide_string_input_adapter { public: using char_type = char; wide_string_input_adapter(BaseInputAdapter base) : base_adapter(base) {} typename std::char_traits::int_type get_character() noexcept { // check if buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) { fill_buffer(); JSON_ASSERT(utf8_bytes_filled > 0); JSON_ASSERT(utf8_bytes_index == 0); } // use buffer JSON_ASSERT(utf8_bytes_filled > 0); JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); return utf8_bytes[utf8_bytes_index++]; } private: BaseInputAdapter base_adapter; template void fill_buffer() { wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); } /// a buffer for UTF-8 bytes std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; /// index to the utf8_codes array for the next valid byte std::size_t utf8_bytes_index = 0; /// number of valid bytes in the utf8_codes array std::size_t utf8_bytes_filled = 0; }; template struct iterator_input_adapter_factory { using iterator_type = IteratorType; using char_type = typename std::iterator_traits::value_type; using adapter_type = iterator_input_adapter; static adapter_type create(IteratorType first, IteratorType last) { return adapter_type(std::move(first), std::move(last)); } }; template struct is_iterator_of_multibyte { using value_type = typename std::iterator_traits::value_type; enum { value = sizeof(value_type) > 1 }; }; template struct iterator_input_adapter_factory::value>> { using iterator_type = IteratorType; using char_type = typename std::iterator_traits::value_type; using base_adapter_type = iterator_input_adapter; using adapter_type = wide_string_input_adapter; static adapter_type create(IteratorType first, IteratorType last) { return adapter_type(base_adapter_type(std::move(first), std::move(last))); } }; // General purpose iterator-based input template typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) { using factory_type = iterator_input_adapter_factory; return factory_type::create(first, last); } // Convenience shorthand from container to iterator // Enables ADL on begin(container) and end(container) // Encloses the using declarations in namespace for not to leak them to outside scope namespace container_input_adapter_factory_impl { using std::begin; using std::end; template struct container_input_adapter_factory {}; template struct container_input_adapter_factory< ContainerType, void_t()), end(std::declval()))>> { using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); static adapter_type create(const ContainerType& container) { return input_adapter(begin(container), end(container)); } }; } // namespace container_input_adapter_factory_impl template typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) { return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); } #ifndef JSON_NO_IO // Special cases with fast paths inline file_input_adapter input_adapter(std::FILE* file) { return file_input_adapter(file); } inline input_stream_adapter input_adapter(std::istream& stream) { return input_stream_adapter(stream); } inline input_stream_adapter input_adapter(std::istream&& stream) { return input_stream_adapter(stream); } #endif // JSON_NO_IO using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); // Null-delimited strings, and the like. template < typename CharT, typename std::enable_if < std::is_pointer::value&& !std::is_array::value&& std::is_integral::type>::value&& sizeof(typename std::remove_pointer::type) == 1, int >::type = 0 > contiguous_bytes_input_adapter input_adapter(CharT b) { auto length = std::strlen(reinterpret_cast(b)); const auto* ptr = reinterpret_cast(b); return input_adapter(ptr, ptr + length); } template auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) { return input_adapter(array, array + N); } // This class only handles inputs of input_buffer_adapter type. // It's required so that expressions like {ptr, len} can be implicitely casted // to the correct adapter. class span_input_adapter { public: template < typename CharT, typename std::enable_if < std::is_pointer::value&& std::is_integral::type>::value&& sizeof(typename std::remove_pointer::type) == 1, int >::type = 0 > span_input_adapter(CharT b, std::size_t l) : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} template::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> span_input_adapter(IteratorType first, IteratorType last) : ia(input_adapter(first, last)) {} contiguous_bytes_input_adapter&& get() { return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) } private: contiguous_bytes_input_adapter ia; }; } // namespace detail } // namespace nlohmann // #include #include #include // string #include // move #include // vector // #include // #include namespace nlohmann { /*! @brief SAX interface This class describes the SAX interface used by @ref nlohmann::json::sax_parse. Each function is called in different situations while the input is parsed. The boolean return value informs the parser whether to continue processing the input. */ template struct json_sax { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; /*! @brief a null value was read @return whether parsing should proceed */ virtual bool null() = 0; /*! @brief a boolean value was read @param[in] val boolean value @return whether parsing should proceed */ virtual bool boolean(bool val) = 0; /*! @brief an integer number was read @param[in] val integer value @return whether parsing should proceed */ virtual bool number_integer(number_integer_t val) = 0; /*! @brief an unsigned integer number was read @param[in] val unsigned integer value @return whether parsing should proceed */ virtual bool number_unsigned(number_unsigned_t val) = 0; /*! @brief an floating-point number was read @param[in] val floating-point value @param[in] s raw token value @return whether parsing should proceed */ virtual bool number_float(number_float_t val, const string_t& s) = 0; /*! @brief a string was read @param[in] val string value @return whether parsing should proceed @note It is safe to move the passed string. */ virtual bool string(string_t& val) = 0; /*! @brief a binary string was read @param[in] val binary value @return whether parsing should proceed @note It is safe to move the passed binary. */ virtual bool binary(binary_t& val) = 0; /*! @brief the beginning of an object was read @param[in] elements number of object elements or -1 if unknown @return whether parsing should proceed @note binary formats may report the number of elements */ virtual bool start_object(std::size_t elements) = 0; /*! @brief an object key was read @param[in] val object key @return whether parsing should proceed @note It is safe to move the passed string. */ virtual bool key(string_t& val) = 0; /*! @brief the end of an object was read @return whether parsing should proceed */ virtual bool end_object() = 0; /*! @brief the beginning of an array was read @param[in] elements number of array elements or -1 if unknown @return whether parsing should proceed @note binary formats may report the number of elements */ virtual bool start_array(std::size_t elements) = 0; /*! @brief the end of an array was read @return whether parsing should proceed */ virtual bool end_array() = 0; /*! @brief a parse error occurred @param[in] position the position in the input where the error occurs @param[in] last_token the last read token @param[in] ex an exception object describing the error @return whether parsing should proceed (must return false) */ virtual bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex) = 0; json_sax() = default; json_sax(const json_sax&) = default; json_sax(json_sax&&) noexcept = default; json_sax& operator=(const json_sax&) = default; json_sax& operator=(json_sax&&) noexcept = default; virtual ~json_sax() = default; }; namespace detail { /*! @brief SAX implementation to create a JSON value from SAX events This class implements the @ref json_sax interface and processes the SAX events to create a JSON value which makes it basically a DOM parser. The structure or hierarchy of the JSON value is managed by the stack `ref_stack` which contains a pointer to the respective array or object for each recursion depth. After successful parsing, the value that is passed by reference to the constructor contains the parsed value. @tparam BasicJsonType the JSON type */ template class json_sax_dom_parser { public: using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; /*! @param[in,out] r reference to a JSON value that is manipulated while parsing @param[in] allow_exceptions_ whether parse errors yield exceptions */ explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) : root(r), allow_exceptions(allow_exceptions_) {} // make class move-only json_sax_dom_parser(const json_sax_dom_parser&) = delete; json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) ~json_sax_dom_parser() = default; bool null() { handle_value(nullptr); return true; } bool boolean(bool val) { handle_value(val); return true; } bool number_integer(number_integer_t val) { handle_value(val); return true; } bool number_unsigned(number_unsigned_t val) { handle_value(val); return true; } bool number_float(number_float_t val, const string_t& /*unused*/) { handle_value(val); return true; } bool string(string_t& val) { handle_value(val); return true; } bool binary(binary_t& val) { handle_value(std::move(val)); return true; } bool start_object(std::size_t len) { ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); } return true; } bool key(string_t& val) { // add null at given key and store the reference for later object_element = &(ref_stack.back()->m_value.object->operator[](val)); return true; } bool end_object() { ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; } bool start_array(std::size_t len) { ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); } return true; } bool end_array() { ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; } template bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const Exception& ex) { errored = true; static_cast(ex); if (allow_exceptions) { JSON_THROW(ex); } return false; } constexpr bool is_errored() const { return errored; } private: /*! @invariant If the ref stack is empty, then the passed value will be the new root. @invariant If the ref stack contains a value, then it is an array or an object to which we can add elements */ template JSON_HEDLEY_RETURNS_NON_NULL BasicJsonType* handle_value(Value&& v) { if (ref_stack.empty()) { root = BasicJsonType(std::forward(v)); return &root; } JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::forward(v)); return &(ref_stack.back()->m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); return object_element; } /// the parsed JSON value BasicJsonType& root; /// stack to model hierarchy of values std::vector ref_stack {}; /// helper to hold the reference for the next object element BasicJsonType* object_element = nullptr; /// whether a syntax error occurred bool errored = false; /// whether to throw exceptions in case of errors const bool allow_exceptions = true; }; template class json_sax_dom_callback_parser { public: using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; json_sax_dom_callback_parser(BasicJsonType& r, const parser_callback_t cb, const bool allow_exceptions_ = true) : root(r), callback(cb), allow_exceptions(allow_exceptions_) { keep_stack.push_back(true); } // make class move-only json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) ~json_sax_dom_callback_parser() = default; bool null() { handle_value(nullptr); return true; } bool boolean(bool val) { handle_value(val); return true; } bool number_integer(number_integer_t val) { handle_value(val); return true; } bool number_unsigned(number_unsigned_t val) { handle_value(val); return true; } bool number_float(number_float_t val, const string_t& /*unused*/) { handle_value(val); return true; } bool string(string_t& val) { handle_value(val); return true; } bool binary(binary_t& val) { handle_value(std::move(val)); return true; } bool start_object(std::size_t len) { // check callback for object start const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); keep_stack.push_back(keep); auto val = handle_value(BasicJsonType::value_t::object, true); ref_stack.push_back(val.second); // check object limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); } return true; } bool key(string_t& val) { BasicJsonType k = BasicJsonType(val); // check callback for key const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); key_keep_stack.push_back(keep); // add discarded value at given key and store the reference for later if (keep && ref_stack.back()) { object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); } return true; } bool end_object() { if (ref_stack.back()) { if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) { // discard object *ref_stack.back() = discarded; } else { ref_stack.back()->set_parents(); } } JSON_ASSERT(!ref_stack.empty()); JSON_ASSERT(!keep_stack.empty()); ref_stack.pop_back(); keep_stack.pop_back(); if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) { // remove discarded value for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) { if (it->is_discarded()) { ref_stack.back()->erase(it); break; } } } return true; } bool start_array(std::size_t len) { const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); keep_stack.push_back(keep); auto val = handle_value(BasicJsonType::value_t::array, true); ref_stack.push_back(val.second); // check array limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); } return true; } bool end_array() { bool keep = true; if (ref_stack.back()) { keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); if (keep) { ref_stack.back()->set_parents(); } else { // discard array *ref_stack.back() = discarded; } } JSON_ASSERT(!ref_stack.empty()); JSON_ASSERT(!keep_stack.empty()); ref_stack.pop_back(); keep_stack.pop_back(); // remove discarded value if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->pop_back(); } return true; } template bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const Exception& ex) { errored = true; static_cast(ex); if (allow_exceptions) { JSON_THROW(ex); } return false; } constexpr bool is_errored() const { return errored; } private: /*! @param[in] v value to add to the JSON value we build during parsing @param[in] skip_callback whether we should skip calling the callback function; this is required after start_array() and start_object() SAX events, because otherwise we would call the callback function with an empty array or object, respectively. @invariant If the ref stack is empty, then the passed value will be the new root. @invariant If the ref stack contains a value, then it is an array or an object to which we can add elements @return pair of boolean (whether value should be kept) and pointer (to the passed value in the ref_stack hierarchy; nullptr if not kept) */ template std::pair handle_value(Value&& v, const bool skip_callback = false) { JSON_ASSERT(!keep_stack.empty()); // do not handle this value if we know it would be added to a discarded // container if (!keep_stack.back()) { return {false, nullptr}; } // create value auto value = BasicJsonType(std::forward(v)); // check callback const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); // do not handle this value if we just learnt it shall be discarded if (!keep) { return {false, nullptr}; } if (ref_stack.empty()) { root = std::move(value); return {true, &root}; } // skip this value if we already decided to skip the parent // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) if (!ref_stack.back()) { return {false, nullptr}; } // we now only expect arrays and objects JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); // array if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::move(value)); return {true, &(ref_stack.back()->m_value.array->back())}; } // object JSON_ASSERT(ref_stack.back()->is_object()); // check if we should store an element for the current key JSON_ASSERT(!key_keep_stack.empty()); const bool store_element = key_keep_stack.back(); key_keep_stack.pop_back(); if (!store_element) { return {false, nullptr}; } JSON_ASSERT(object_element); *object_element = std::move(value); return {true, object_element}; } /// the parsed JSON value BasicJsonType& root; /// stack to model hierarchy of values std::vector ref_stack {}; /// stack to manage which values to keep std::vector keep_stack {}; /// stack to manage which object keys to keep std::vector key_keep_stack {}; /// helper to hold the reference for the next object element BasicJsonType* object_element = nullptr; /// whether a syntax error occurred bool errored = false; /// callback function const parser_callback_t callback = nullptr; /// whether to throw exceptions in case of errors const bool allow_exceptions = true; /// a discarded value for the callback BasicJsonType discarded = BasicJsonType::value_t::discarded; }; template class json_sax_acceptor { public: using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; bool null() { return true; } bool boolean(bool /*unused*/) { return true; } bool number_integer(number_integer_t /*unused*/) { return true; } bool number_unsigned(number_unsigned_t /*unused*/) { return true; } bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) { return true; } bool string(string_t& /*unused*/) { return true; } bool binary(binary_t& /*unused*/) { return true; } bool start_object(std::size_t /*unused*/ = std::size_t(-1)) { return true; } bool key(string_t& /*unused*/) { return true; } bool end_object() { return true; } bool start_array(std::size_t /*unused*/ = std::size_t(-1)) { return true; } bool end_array() { return true; } bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) { return false; } }; } // namespace detail } // namespace nlohmann // #include #include // array #include // localeconv #include // size_t #include // snprintf #include // strtof, strtod, strtold, strtoll, strtoull #include // initializer_list #include // char_traits, string #include // move #include // vector // #include // #include // #include namespace nlohmann { namespace detail { /////////// // lexer // /////////// template class lexer_base { public: /// token types for the parser enum class token_type { uninitialized, ///< indicating the scanner is uninitialized literal_true, ///< the `true` literal literal_false, ///< the `false` literal literal_null, ///< the `null` literal value_string, ///< a string -- use get_string() for actual value value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value value_integer, ///< a signed integer -- use get_number_integer() for actual value value_float, ///< an floating point number -- use get_number_float() for actual value begin_array, ///< the character for array begin `[` begin_object, ///< the character for object begin `{` end_array, ///< the character for array end `]` end_object, ///< the character for object end `}` name_separator, ///< the name separator `:` value_separator, ///< the value separator `,` parse_error, ///< indicating a parse error end_of_input, ///< indicating the end of the input buffer literal_or_value ///< a literal or the begin of a value (only for diagnostics) }; /// return name of values of type token_type (only used for errors) JSON_HEDLEY_RETURNS_NON_NULL JSON_HEDLEY_CONST static const char* token_type_name(const token_type t) noexcept { switch (t) { case token_type::uninitialized: return ""; case token_type::literal_true: return "true literal"; case token_type::literal_false: return "false literal"; case token_type::literal_null: return "null literal"; case token_type::value_string: return "string literal"; case token_type::value_unsigned: case token_type::value_integer: case token_type::value_float: return "number literal"; case token_type::begin_array: return "'['"; case token_type::begin_object: return "'{'"; case token_type::end_array: return "']'"; case token_type::end_object: return "'}'"; case token_type::name_separator: return "':'"; case token_type::value_separator: return "','"; case token_type::parse_error: return ""; case token_type::end_of_input: return "end of input"; case token_type::literal_or_value: return "'[', '{', or a literal"; // LCOV_EXCL_START default: // catch non-enum values return "unknown token"; // LCOV_EXCL_STOP } } }; /*! @brief lexical analysis This class organizes the lexical analysis during JSON deserialization. */ template class lexer : public lexer_base { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using char_type = typename InputAdapterType::char_type; using char_int_type = typename std::char_traits::int_type; public: using token_type = typename lexer_base::token_type; explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept : ia(std::move(adapter)) , ignore_comments(ignore_comments_) , decimal_point_char(static_cast(get_decimal_point())) {} // delete because of pointer members lexer(const lexer&) = delete; lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) lexer& operator=(lexer&) = delete; lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) ~lexer() = default; private: ///////////////////// // locales ///////////////////// /// return the locale-dependent decimal point JSON_HEDLEY_PURE static char get_decimal_point() noexcept { const auto* loc = localeconv(); JSON_ASSERT(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } ///////////////////// // scan functions ///////////////////// /*! @brief get codepoint from 4 hex characters following `\u` For input "\u c1 c2 c3 c4" the codepoint is: (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The conversion is done by subtracting the offset (0x30, 0x37, and 0x57) between the ASCII value of the character and the desired integer value. @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or non-hex character) */ int get_codepoint() { // this function only makes sense after reading `\u` JSON_ASSERT(current == 'u'); int codepoint = 0; const auto factors = { 12u, 8u, 4u, 0u }; for (const auto factor : factors) { get(); if (current >= '0' && current <= '9') { codepoint += static_cast((static_cast(current) - 0x30u) << factor); } else if (current >= 'A' && current <= 'F') { codepoint += static_cast((static_cast(current) - 0x37u) << factor); } else if (current >= 'a' && current <= 'f') { codepoint += static_cast((static_cast(current) - 0x57u) << factor); } else { return -1; } } JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); return codepoint; } /*! @brief check if the next byte(s) are inside a given range Adds the current byte and, for each passed range, reads a new byte and checks if it is inside the range. If a violation was detected, set up an error message and return false. Otherwise, return true. @param[in] ranges list of integers; interpreted as list of pairs of inclusive lower and upper bound, respectively @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, 1, 2, or 3 pairs. This precondition is enforced by an assertion. @return true if and only if no range violation was detected */ bool next_byte_in_range(std::initializer_list ranges) { JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); add(current); for (auto range = ranges.begin(); range != ranges.end(); ++range) { get(); if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) { add(current); } else { error_message = "invalid string: ill-formed UTF-8 byte"; return false; } } return true; } /*! @brief scan a string literal This function scans a string according to Sect. 7 of RFC 8259. While scanning, bytes are escaped and copied into buffer token_buffer. Then the function returns successfully, token_buffer is *not* null-terminated (as it may contain \0 bytes), and token_buffer.size() is the number of bytes in the string. @return token_type::value_string if string could be successfully scanned, token_type::parse_error otherwise @note In case of errors, variable error_message contains a textual description. */ token_type scan_string() { // reset token_buffer (ignore opening quote) reset(); // we entered the function by reading an open quote JSON_ASSERT(current == '\"'); while (true) { // get next character switch (get()) { // end of file while parsing string case std::char_traits::eof(): { error_message = "invalid string: missing closing quote"; return token_type::parse_error; } // closing quote case '\"': { return token_type::value_string; } // escapes case '\\': { switch (get()) { // quotation mark case '\"': add('\"'); break; // reverse solidus case '\\': add('\\'); break; // solidus case '/': add('/'); break; // backspace case 'b': add('\b'); break; // form feed case 'f': add('\f'); break; // line feed case 'n': add('\n'); break; // carriage return case 'r': add('\r'); break; // tab case 't': add('\t'); break; // unicode escapes case 'u': { const int codepoint1 = get_codepoint(); int codepoint = codepoint1; // start with codepoint1 if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if code point is a high surrogate if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) { // expect next \uxxxx entry if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) { const int codepoint2 = get_codepoint(); if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if codepoint2 is a low surrogate if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) { // overwrite codepoint codepoint = static_cast( // high surrogate occupies the most significant 22 bits (static_cast(codepoint1) << 10u) // low surrogate occupies the least significant 15 bits + static_cast(codepoint2) // there is still the 0xD800, 0xDC00 and 0x10000 noise // in the result so we have to subtract with: // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - 0x35FDC00u); } else { error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) { error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; return token_type::parse_error; } } // result of the above calculation yields a proper codepoint JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); // translate codepoint into bytes if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) add(static_cast(codepoint)); } else if (codepoint <= 0x7FF) { // 2-byte characters: 110xxxxx 10xxxxxx add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); } else if (codepoint <= 0xFFFF) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); } else { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); } break; } // other characters after escape default: error_message = "invalid string: forbidden character after backslash"; return token_type::parse_error; } break; } // invalid control characters case 0x00: { error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; return token_type::parse_error; } case 0x01: { error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; return token_type::parse_error; } case 0x02: { error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; return token_type::parse_error; } case 0x03: { error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; return token_type::parse_error; } case 0x04: { error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; return token_type::parse_error; } case 0x05: { error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; return token_type::parse_error; } case 0x06: { error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; return token_type::parse_error; } case 0x07: { error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; return token_type::parse_error; } case 0x08: { error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; return token_type::parse_error; } case 0x09: { error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; return token_type::parse_error; } case 0x0A: { error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; return token_type::parse_error; } case 0x0B: { error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; return token_type::parse_error; } case 0x0C: { error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; return token_type::parse_error; } case 0x0D: { error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; return token_type::parse_error; } case 0x0E: { error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; return token_type::parse_error; } case 0x0F: { error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; return token_type::parse_error; } case 0x10: { error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; return token_type::parse_error; } case 0x11: { error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; return token_type::parse_error; } case 0x12: { error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; return token_type::parse_error; } case 0x13: { error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; return token_type::parse_error; } case 0x14: { error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; return token_type::parse_error; } case 0x15: { error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; return token_type::parse_error; } case 0x16: { error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; return token_type::parse_error; } case 0x17: { error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; return token_type::parse_error; } case 0x18: { error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; return token_type::parse_error; } case 0x19: { error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; return token_type::parse_error; } case 0x1A: { error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; return token_type::parse_error; } case 0x1B: { error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; return token_type::parse_error; } case 0x1C: { error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; return token_type::parse_error; } case 0x1D: { error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; return token_type::parse_error; } case 0x1E: { error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; return token_type::parse_error; } case 0x1F: { error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; return token_type::parse_error; } // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) case 0x20: case 0x21: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5D: case 0x5E: case 0x5F: case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: { add(current); break; } // U+0080..U+07FF: bytes C2..DF 80..BF case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7: case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: case 0xD7: case 0xD8: case 0xD9: case 0xDA: case 0xDB: case 0xDC: case 0xDD: case 0xDE: case 0xDF: { if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) { return token_type::parse_error; } break; } // U+0800..U+0FFF: bytes E0 A0..BF 80..BF case 0xE0: { if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7: case 0xE8: case 0xE9: case 0xEA: case 0xEB: case 0xEC: case 0xEE: case 0xEF: { if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+D000..U+D7FF: bytes ED 80..9F 80..BF case 0xED: { if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF case 0xF0: { if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF case 0xF1: case 0xF2: case 0xF3: { if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF case 0xF4: { if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // remaining bytes (80..C1 and F5..FF) are ill-formed default: { error_message = "invalid string: ill-formed UTF-8 byte"; return token_type::parse_error; } } } } /*! * @brief scan a comment * @return whether comment could be scanned successfully */ bool scan_comment() { switch (get()) { // single-line comments skip input until a newline or EOF is read case '/': { while (true) { switch (get()) { case '\n': case '\r': case std::char_traits::eof(): case '\0': return true; default: break; } } } // multi-line comments skip input until */ is read case '*': { while (true) { switch (get()) { case std::char_traits::eof(): case '\0': { error_message = "invalid comment; missing closing '*/'"; return false; } case '*': { switch (get()) { case '/': return true; default: { unget(); continue; } } } default: continue; } } } // unexpected character after reading '/' default: { error_message = "invalid comment; expecting '/' or '*' after '/'"; return false; } } } JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { f = std::strtof(str, endptr); } JSON_HEDLEY_NON_NULL(2) static void strtof(double& f, const char* str, char** endptr) noexcept { f = std::strtod(str, endptr); } JSON_HEDLEY_NON_NULL(2) static void strtof(long double& f, const char* str, char** endptr) noexcept { f = std::strtold(str, endptr); } /*! @brief scan a number literal This function scans a string according to Sect. 6 of RFC 8259. The function is realized with a deterministic finite state machine derived from the grammar described in RFC 8259. Starting in state "d2Init", the input is read and used to determined the next state. Only state "done" accepts the number. State "error" is a trap state to model errors. In the table below, "anything" means any character but the ones listed before. state | 0 | 1-9 | e E | + | - | . | anything ---------|----------|----------|----------|---------|---------|----------|----------- d2Init | zero | any1 | [error] | [error] | minus | [error] | [error] minus | zero | any1 | [error] | [error] | [error] | [error] | [error] zero | done | done | exponent | done | done | decimal1 | done any1 | any1 | any1 | exponent | done | done | decimal1 | done decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] decimal2 | decimal2 | decimal2 | exponent | done | done | done | done exponent | any2 | any2 | [error] | sign | sign | [error] | [error] sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] any2 | any2 | any2 | done | done | done | done | done The state machine is realized with one label per state (prefixed with "scan_number_") and `goto` statements between them. The state machine contains cycles, but any cycle can be left when EOF is read. Therefore, the function is guaranteed to terminate. During scanning, the read bytes are stored in token_buffer. This string is then converted to a signed integer, an unsigned integer, or a floating-point number. @return token_type::value_unsigned, token_type::value_integer, or token_type::value_float if number could be successfully scanned, token_type::parse_error otherwise @note The scanner is independent of the current locale. Internally, the locale's decimal point is used instead of `.` to work with the locale-dependent converters. */ token_type scan_number() // lgtm [cpp/use-of-goto] { // reset token_buffer to store the number's bytes reset(); // the type of the parsed number; initially set to unsigned; will be // changed if minus sign, decimal point or exponent is read token_type number_type = token_type::value_unsigned; // state (init): we just found out we need to scan a number switch (current) { case '-': { add(current); goto scan_number_minus; } case '0': { add(current); goto scan_number_zero; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any1; } // all other characters are rejected outside scan_number() default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } scan_number_minus: // state: we just parsed a leading minus sign number_type = token_type::value_integer; switch (get()) { case '0': { add(current); goto scan_number_zero; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any1; } default: { error_message = "invalid number; expected digit after '-'"; return token_type::parse_error; } } scan_number_zero: // state: we just parse a zero (maybe with a leading minus sign) switch (get()) { case '.': { add(decimal_point_char); goto scan_number_decimal1; } case 'e': case 'E': { add(current); goto scan_number_exponent; } default: goto scan_number_done; } scan_number_any1: // state: we just parsed a number 0-9 (maybe with a leading minus sign) switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any1; } case '.': { add(decimal_point_char); goto scan_number_decimal1; } case 'e': case 'E': { add(current); goto scan_number_exponent; } default: goto scan_number_done; } scan_number_decimal1: // state: we just parsed a decimal point number_type = token_type::value_float; switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_decimal2; } default: { error_message = "invalid number; expected digit after '.'"; return token_type::parse_error; } } scan_number_decimal2: // we just parsed at least one number after a decimal point switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_decimal2; } case 'e': case 'E': { add(current); goto scan_number_exponent; } default: goto scan_number_done; } scan_number_exponent: // we just parsed an exponent number_type = token_type::value_float; switch (get()) { case '+': case '-': { add(current); goto scan_number_sign; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any2; } default: { error_message = "invalid number; expected '+', '-', or digit after exponent"; return token_type::parse_error; } } scan_number_sign: // we just parsed an exponent sign switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any2; } default: { error_message = "invalid number; expected digit after exponent sign"; return token_type::parse_error; } } scan_number_any2: // we just parsed a number after the exponent or exponent sign switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any2; } default: goto scan_number_done; } scan_number_done: // unget the character after the number (we only read it to know that // we are done scanning a number) unget(); char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) errno = 0; // try to parse integers first and fall back to floats if (number_type == token_type::value_unsigned) { const auto x = std::strtoull(token_buffer.data(), &endptr, 10); // we checked the number format before JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { value_unsigned = static_cast(x); if (value_unsigned == x) { return token_type::value_unsigned; } } } else if (number_type == token_type::value_integer) { const auto x = std::strtoll(token_buffer.data(), &endptr, 10); // we checked the number format before JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { value_integer = static_cast(x); if (value_integer == x) { return token_type::value_integer; } } } // this code is reached if we parse a floating-point number or if an // integer conversion above failed strtof(value_float, token_buffer.data(), &endptr); // we checked the number format before JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); return token_type::value_float; } /*! @param[in] literal_text the literal text to expect @param[in] length the length of the passed literal text @param[in] return_type the token type to return on success */ JSON_HEDLEY_NON_NULL(2) token_type scan_literal(const char_type* literal_text, const std::size_t length, token_type return_type) { JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); for (std::size_t i = 1; i < length; ++i) { if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) { error_message = "invalid literal"; return token_type::parse_error; } } return return_type; } ///////////////////// // input management ///////////////////// /// reset token_buffer; current character is beginning of token void reset() noexcept { token_buffer.clear(); token_string.clear(); token_string.push_back(std::char_traits::to_char_type(current)); } /* @brief get next character from the input This function provides the interface to the used input adapter. It does not throw in case the input reached EOF, but returns a `std::char_traits::eof()` in that case. Stores the scanned characters for use in error messages. @return character read from the input */ char_int_type get() { ++position.chars_read_total; ++position.chars_read_current_line; if (next_unget) { // just reset the next_unget variable and work with current next_unget = false; } else { current = ia.get_character(); } if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) { token_string.push_back(std::char_traits::to_char_type(current)); } if (current == '\n') { ++position.lines_read; position.chars_read_current_line = 0; } return current; } /*! @brief unget current character (read it again on next get) We implement unget by setting variable next_unget to true. The input is not changed - we just simulate ungetting by modifying chars_read_total, chars_read_current_line, and token_string. The next call to get() will behave as if the unget character is read again. */ void unget() { next_unget = true; --position.chars_read_total; // in case we "unget" a newline, we have to also decrement the lines_read if (position.chars_read_current_line == 0) { if (position.lines_read > 0) { --position.lines_read; } } else { --position.chars_read_current_line; } if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) { JSON_ASSERT(!token_string.empty()); token_string.pop_back(); } } /// add a character to token_buffer void add(char_int_type c) { token_buffer.push_back(static_cast(c)); } public: ///////////////////// // value getters ///////////////////// /// return integer value constexpr number_integer_t get_number_integer() const noexcept { return value_integer; } /// return unsigned integer value constexpr number_unsigned_t get_number_unsigned() const noexcept { return value_unsigned; } /// return floating-point value constexpr number_float_t get_number_float() const noexcept { return value_float; } /// return current string value (implicitly resets the token; useful only once) string_t& get_string() { return token_buffer; } ///////////////////// // diagnostics ///////////////////// /// return position of last read token constexpr position_t get_position() const noexcept { return position; } /// return the last read token (for errors only). Will never contain EOF /// (an arbitrary value that is not a valid char value, often -1), because /// 255 may legitimately occur. May contain NUL, which should be escaped. std::string get_token_string() const { // escape control characters std::string result; for (const auto c : token_string) { if (static_cast(c) <= '\x1F') { // escape control characters std::array cs{{}}; (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) result += cs.data(); } else { // add character as is result.push_back(static_cast(c)); } } return result; } /// return syntax error message JSON_HEDLEY_RETURNS_NON_NULL constexpr const char* get_error_message() const noexcept { return error_message; } ///////////////////// // actual scanner ///////////////////// /*! @brief skip the UTF-8 byte order mark @return true iff there is no BOM or the correct BOM has been skipped */ bool skip_bom() { if (get() == 0xEF) { // check if we completely parse the BOM return get() == 0xBB && get() == 0xBF; } // the first character is not the beginning of the BOM; unget it to // process is later unget(); return true; } void skip_whitespace() { do { get(); } while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); } token_type scan() { // initially, skip the BOM if (position.chars_read_total == 0 && !skip_bom()) { error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; return token_type::parse_error; } // read next character and ignore whitespace skip_whitespace(); // ignore comments while (ignore_comments && current == '/') { if (!scan_comment()) { return token_type::parse_error; } // skip following whitespace skip_whitespace(); } switch (current) { // structural characters case '[': return token_type::begin_array; case ']': return token_type::end_array; case '{': return token_type::begin_object; case '}': return token_type::end_object; case ':': return token_type::name_separator; case ',': return token_type::value_separator; // literals case 't': { std::array true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}}; return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); } case 'f': { std::array false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}}; return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); } case 'n': { std::array null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}}; return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); } // string case '\"': return scan_string(); // number case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return scan_number(); // end of input (the null byte is needed when parsing from // string literals) case '\0': case std::char_traits::eof(): return token_type::end_of_input; // error default: error_message = "invalid literal"; return token_type::parse_error; } } private: /// input adapter InputAdapterType ia; /// whether comments should be ignored (true) or signaled as errors (false) const bool ignore_comments = false; /// the current character char_int_type current = std::char_traits::eof(); /// whether the next get() call should just return current bool next_unget = false; /// the start position of the current token position_t position {}; /// raw input token string (for error messages) std::vector token_string {}; /// buffer for variable-length tokens (numbers, strings) string_t token_buffer {}; /// a description of occurred lexer errors const char* error_message = ""; // number values number_integer_t value_integer = 0; number_unsigned_t value_unsigned = 0; number_float_t value_float = 0; /// the decimal point const char_int_type decimal_point_char = '.'; }; } // namespace detail } // namespace nlohmann // #include // #include #include // size_t #include // declval #include // string // #include // #include namespace nlohmann { namespace detail { template using null_function_t = decltype(std::declval().null()); template using boolean_function_t = decltype(std::declval().boolean(std::declval())); template using number_integer_function_t = decltype(std::declval().number_integer(std::declval())); template using number_unsigned_function_t = decltype(std::declval().number_unsigned(std::declval())); template using number_float_function_t = decltype(std::declval().number_float( std::declval(), std::declval())); template using string_function_t = decltype(std::declval().string(std::declval())); template using binary_function_t = decltype(std::declval().binary(std::declval())); template using start_object_function_t = decltype(std::declval().start_object(std::declval())); template using key_function_t = decltype(std::declval().key(std::declval())); template using end_object_function_t = decltype(std::declval().end_object()); template using start_array_function_t = decltype(std::declval().start_array(std::declval())); template using end_array_function_t = decltype(std::declval().end_array()); template using parse_error_function_t = decltype(std::declval().parse_error( std::declval(), std::declval(), std::declval())); template struct is_sax { private: static_assert(is_basic_json::value, "BasicJsonType must be of type basic_json<...>"); using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; using exception_t = typename BasicJsonType::exception; public: static constexpr bool value = is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value && is_detected_exact::value; }; template struct is_sax_static_asserts { private: static_assert(is_basic_json::value, "BasicJsonType must be of type basic_json<...>"); using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; using exception_t = typename BasicJsonType::exception; public: static_assert(is_detected_exact::value, "Missing/invalid function: bool null()"); static_assert(is_detected_exact::value, "Missing/invalid function: bool boolean(bool)"); static_assert(is_detected_exact::value, "Missing/invalid function: bool boolean(bool)"); static_assert( is_detected_exact::value, "Missing/invalid function: bool number_integer(number_integer_t)"); static_assert( is_detected_exact::value, "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); static_assert(is_detected_exact::value, "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); static_assert( is_detected_exact::value, "Missing/invalid function: bool string(string_t&)"); static_assert( is_detected_exact::value, "Missing/invalid function: bool binary(binary_t&)"); static_assert(is_detected_exact::value, "Missing/invalid function: bool start_object(std::size_t)"); static_assert(is_detected_exact::value, "Missing/invalid function: bool key(string_t&)"); static_assert(is_detected_exact::value, "Missing/invalid function: bool end_object()"); static_assert(is_detected_exact::value, "Missing/invalid function: bool start_array(std::size_t)"); static_assert(is_detected_exact::value, "Missing/invalid function: bool end_array()"); static_assert( is_detected_exact::value, "Missing/invalid function: bool parse_error(std::size_t, const " "std::string&, const exception&)"); }; } // namespace detail } // namespace nlohmann // #include // #include namespace nlohmann { namespace detail { /// how to treat CBOR tags enum class cbor_tag_handler_t { error, ///< throw a parse_error exception in case of a tag ignore, ///< ignore tags store ///< store tags as binary type }; /*! @brief determine system byte order @return true if and only if system's byte order is little endian @note from https://stackoverflow.com/a/1001328/266378 */ static inline bool little_endianess(int num = 1) noexcept { return *reinterpret_cast(&num) == 1; } /////////////////// // binary reader // /////////////////// /*! @brief deserialization of CBOR, MessagePack, and UBJSON values */ template> class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; using json_sax_t = SAX; using char_type = typename InputAdapterType::char_type; using char_int_type = typename std::char_traits::int_type; public: /*! @brief create a binary reader @param[in] adapter input adapter to read from */ explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) { (void)detail::is_sax_static_asserts {}; } // make class move-only binary_reader(const binary_reader&) = delete; binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) binary_reader& operator=(const binary_reader&) = delete; binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) ~binary_reader() = default; /*! @param[in] format the binary format to parse @param[in] sax_ a SAX event processor @param[in] strict whether to expect the input to be consumed completed @param[in] tag_handler how to treat CBOR tags @return whether parsing was successful */ JSON_HEDLEY_NON_NULL(3) bool sax_parse(const input_format_t format, json_sax_t* sax_, const bool strict = true, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { sax = sax_; bool result = false; switch (format) { case input_format_t::bson: result = parse_bson_internal(); break; case input_format_t::cbor: result = parse_cbor_internal(true, tag_handler); break; case input_format_t::msgpack: result = parse_msgpack_internal(); break; case input_format_t::ubjson: result = parse_ubjson_internal(); break; case input_format_t::json: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } // strict mode: next byte must be EOF if (result && strict) { if (format == input_format_t::ubjson) { get_ignore_noop(); } else { get(); } if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); } } return result; } private: ////////// // BSON // ////////// /*! @brief Reads in a BSON-object and passes it to the SAX-parser. @return whether a valid BSON-value was passed to the SAX parser */ bool parse_bson_internal() { std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) { return false; } if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) { return false; } return sax->end_object(); } /*! @brief Parses a C-style string from the BSON input. @param[in,out] result A reference to the string variable where the read string is to be stored. @return `true` if the \x00-byte indicating the end of the string was encountered before the EOF; false` indicates an unexpected EOF. */ bool get_bson_cstr(string_t& result) { auto out = std::back_inserter(result); while (true) { get(); if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) { return false; } if (current == 0x00) { return true; } *out++ = static_cast(current); } } /*! @brief Parses a zero-terminated string of length @a len from the BSON input. @param[in] len The length (including the zero-byte at the end) of the string to be read. @param[in,out] result A reference to the string variable where the read string is to be stored. @tparam NumberType The type of the length @a len @pre len >= 1 @return `true` if the string was successfully parsed */ template bool get_bson_string(const NumberType len, string_t& result) { if (JSON_HEDLEY_UNLIKELY(len < 1)) { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); } return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); } /*! @brief Parses a byte array input of length @a len from the BSON input. @param[in] len The length of the byte array to be read. @param[in,out] result A reference to the binary variable where the read array is to be stored. @tparam NumberType The type of the length @a len @pre len >= 0 @return `true` if the byte array was successfully parsed */ template bool get_bson_binary(const NumberType len, binary_t& result) { if (JSON_HEDLEY_UNLIKELY(len < 0)) { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); } // All BSON binary values have a subtype std::uint8_t subtype{}; get_number(input_format_t::bson, subtype); result.set_subtype(subtype); return get_binary(input_format_t::bson, len, result); } /*! @brief Read a BSON document element of the given @a element_type. @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html @param[in] element_type_parse_position The position in the input stream, where the `element_type` was read. @warning Not all BSON element types are supported yet. An unsupported @a element_type will give rise to a parse_error.114: Unsupported BSON record type 0x... @return whether a valid BSON-object/array was passed to the SAX parser */ bool parse_bson_element_internal(const char_int_type element_type, const std::size_t element_type_parse_position) { switch (element_type) { case 0x01: // double { double number{}; return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); } case 0x02: // string { std::int32_t len{}; string_t value; return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); } case 0x03: // object { return parse_bson_internal(); } case 0x04: // array { return parse_bson_array(); } case 0x05: // binary { std::int32_t len{}; binary_t value; return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); } case 0x08: // boolean { return sax->boolean(get() != 0); } case 0x0A: // null { return sax->null(); } case 0x10: // int32 { std::int32_t value{}; return get_number(input_format_t::bson, value) && sax->number_integer(value); } case 0x12: // int64 { std::int64_t value{}; return get_number(input_format_t::bson, value) && sax->number_integer(value); } default: // anything else not supported (yet) { std::array cr{{}}; (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); } } } /*! @brief Read a BSON element list (as specified in the BSON-spec) The same binary layout is used for objects and arrays, hence it must be indicated with the argument @a is_array which one is expected (true --> array, false --> object). @param[in] is_array Determines if the element list being read is to be treated as an object (@a is_array == false), or as an array (@a is_array == true). @return whether a valid BSON-object/array was passed to the SAX parser */ bool parse_bson_element_list(const bool is_array) { string_t key; while (auto element_type = get()) { if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) { return false; } const std::size_t element_type_parse_position = chars_read; if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) { return false; } if (!is_array && !sax->key(key)) { return false; } if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) { return false; } // get_bson_cstr only appends key.clear(); } return true; } /*! @brief Reads an array from the BSON input and passes it to the SAX-parser. @return whether a valid BSON-array was passed to the SAX parser */ bool parse_bson_array() { std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) { return false; } if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) { return false; } return sax->end_array(); } ////////// // CBOR // ////////// /*! @param[in] get_char whether a new character should be retrieved from the input (true) or whether the last read character should be considered instead (false) @param[in] tag_handler how CBOR tags should be treated @return whether a valid CBOR value was passed to the SAX parser */ bool parse_cbor_internal(const bool get_char, const cbor_tag_handler_t tag_handler) { switch (get_char ? get() : current) { // EOF case std::char_traits::eof(): return unexpect_eof(input_format_t::cbor, "value"); // Integer 0x00..0x17 (0..23) case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: return sax->number_unsigned(static_cast(current)); case 0x18: // Unsigned integer (one-byte uint8_t follows) { std::uint8_t number{}; return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); } case 0x19: // Unsigned integer (two-byte uint16_t follows) { std::uint16_t number{}; return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); } case 0x1A: // Unsigned integer (four-byte uint32_t follows) { std::uint32_t number{}; return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); } case 0x1B: // Unsigned integer (eight-byte uint64_t follows) { std::uint64_t number{}; return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); } // Negative integer -1-0x00..-1-0x17 (-1..-24) case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: return sax->number_integer(static_cast(0x20 - 1 - current)); case 0x38: // Negative integer (one-byte uint8_t follows) { std::uint8_t number{}; return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); } case 0x39: // Negative integer -1-n (two-byte uint16_t follows) { std::uint16_t number{}; return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); } case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) { std::uint32_t number{}; return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); } case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) { std::uint64_t number{}; return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - static_cast(number)); } // Binary data (0x00..0x17 bytes follow) case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: // Binary data (one-byte uint8_t for n follows) case 0x59: // Binary data (two-byte uint16_t for n follow) case 0x5A: // Binary data (four-byte uint32_t for n follow) case 0x5B: // Binary data (eight-byte uint64_t for n follow) case 0x5F: // Binary data (indefinite length) { binary_t b; return get_cbor_binary(b) && sax->binary(b); } // UTF-8 string (0x00..0x17 bytes follow) case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: // UTF-8 string (one-byte uint8_t for n follows) case 0x79: // UTF-8 string (two-byte uint16_t for n follow) case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) case 0x7F: // UTF-8 string (indefinite length) { string_t s; return get_cbor_string(s) && sax->string(s); } // array (0x00..0x17 data items follow) case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: return get_cbor_array(static_cast(static_cast(current) & 0x1Fu), tag_handler); case 0x98: // array (one-byte uint8_t for n follows) { std::uint8_t len{}; return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); } case 0x99: // array (two-byte uint16_t for n follow) { std::uint16_t len{}; return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); } case 0x9A: // array (four-byte uint32_t for n follow) { std::uint32_t len{}; return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); } case 0x9B: // array (eight-byte uint64_t for n follow) { std::uint64_t len{}; return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast(len), tag_handler); } case 0x9F: // array (indefinite length) return get_cbor_array(std::size_t(-1), tag_handler); // map (0x00..0x17 pairs of data items follow) case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7: case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF: case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: return get_cbor_object(static_cast(static_cast(current) & 0x1Fu), tag_handler); case 0xB8: // map (one-byte uint8_t for n follows) { std::uint8_t len{}; return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); } case 0xB9: // map (two-byte uint16_t for n follow) { std::uint16_t len{}; return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); } case 0xBA: // map (four-byte uint32_t for n follow) { std::uint32_t len{}; return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); } case 0xBB: // map (eight-byte uint64_t for n follow) { std::uint64_t len{}; return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast(len), tag_handler); } case 0xBF: // map (indefinite length) return get_cbor_object(std::size_t(-1), tag_handler); case 0xC6: // tagged item case 0xC7: case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD8: // tagged item (1 bytes follow) case 0xD9: // tagged item (2 bytes follow) case 0xDA: // tagged item (4 bytes follow) case 0xDB: // tagged item (8 bytes follow) { switch (tag_handler) { case cbor_tag_handler_t::error: { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } case cbor_tag_handler_t::ignore: { // ignore binary subtype switch (current) { case 0xD8: { std::uint8_t subtype_to_ignore{}; get_number(input_format_t::cbor, subtype_to_ignore); break; } case 0xD9: { std::uint16_t subtype_to_ignore{}; get_number(input_format_t::cbor, subtype_to_ignore); break; } case 0xDA: { std::uint32_t subtype_to_ignore{}; get_number(input_format_t::cbor, subtype_to_ignore); break; } case 0xDB: { std::uint64_t subtype_to_ignore{}; get_number(input_format_t::cbor, subtype_to_ignore); break; } default: break; } return parse_cbor_internal(true, tag_handler); } case cbor_tag_handler_t::store: { binary_t b; // use binary subtype and store in binary container switch (current) { case 0xD8: { std::uint8_t subtype{}; get_number(input_format_t::cbor, subtype); b.set_subtype(detail::conditional_static_cast(subtype)); break; } case 0xD9: { std::uint16_t subtype{}; get_number(input_format_t::cbor, subtype); b.set_subtype(detail::conditional_static_cast(subtype)); break; } case 0xDA: { std::uint32_t subtype{}; get_number(input_format_t::cbor, subtype); b.set_subtype(detail::conditional_static_cast(subtype)); break; } case 0xDB: { std::uint64_t subtype{}; get_number(input_format_t::cbor, subtype); b.set_subtype(detail::conditional_static_cast(subtype)); break; } default: return parse_cbor_internal(true, tag_handler); } get(); return get_cbor_binary(b) && sax->binary(b); } default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE return false; // LCOV_EXCL_LINE } } case 0xF4: // false return sax->boolean(false); case 0xF5: // true return sax->boolean(true); case 0xF6: // null return sax->null(); case 0xF9: // Half-Precision Float (two-byte IEEE 754) { const auto byte1_raw = get(); if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) { return false; } const auto byte2_raw = get(); if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) { return false; } const auto byte1 = static_cast(byte1_raw); const auto byte2 = static_cast(byte2_raw); // code from RFC 7049, Appendix D, Figure 3: // As half-precision floating-point numbers were only added // to IEEE 754 in 2008, today's programming platforms often // still only have limited support for them. It is very // easy to include at least decoding support for them even // without such support. An example of a small decoder for // half-precision floating-point numbers in the C language // is shown in Fig. 3. const auto half = static_cast((byte1 << 8u) + byte2); const double val = [&half] { const int exp = (half >> 10u) & 0x1Fu; const unsigned int mant = half & 0x3FFu; JSON_ASSERT(0 <= exp&& exp <= 32); JSON_ASSERT(mant <= 1024); switch (exp) { case 0: return std::ldexp(mant, -24); case 31: return (mant == 0) ? std::numeric_limits::infinity() : std::numeric_limits::quiet_NaN(); default: return std::ldexp(mant + 1024, exp - 25); } }(); return sax->number_float((half & 0x8000u) != 0 ? static_cast(-val) : static_cast(val), ""); } case 0xFA: // Single-Precision Float (four-byte IEEE 754) { float number{}; return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); } case 0xFB: // Double-Precision Float (eight-byte IEEE 754) { double number{}; return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); } default: // anything else (0xFF is handled inside the other types) { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } /*! @brief reads a CBOR string This function first reads starting bytes to determine the expected string length and then copies this number of bytes into a string. Additionally, CBOR's strings with indefinite lengths are supported. @param[out] result created string @return whether string creation completed */ bool get_cbor_string(string_t& result) { if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) { return false; } switch (current) { // UTF-8 string (0x00..0x17 bytes follow) case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: { return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); } case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { std::uint8_t len{}; return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); } case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { std::uint16_t len{}; return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); } case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) { std::uint32_t len{}; return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); } case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { std::uint64_t len{}; return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); } case 0x7F: // UTF-8 string (indefinite length) { while (get() != 0xFF) { string_t chunk; if (!get_cbor_string(chunk)) { return false; } result.append(chunk); } return true; } default: { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); } } } /*! @brief reads a CBOR byte array This function first reads starting bytes to determine the expected byte array length and then copies this number of bytes into the byte array. Additionally, CBOR's byte arrays with indefinite lengths are supported. @param[out] result created byte array @return whether byte array creation completed */ bool get_cbor_binary(binary_t& result) { if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) { return false; } switch (current) { // Binary data (0x00..0x17 bytes follow) case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: { return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); } case 0x58: // Binary data (one-byte uint8_t for n follows) { std::uint8_t len{}; return get_number(input_format_t::cbor, len) && get_binary(input_format_t::cbor, len, result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { std::uint16_t len{}; return get_number(input_format_t::cbor, len) && get_binary(input_format_t::cbor, len, result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { std::uint32_t len{}; return get_number(input_format_t::cbor, len) && get_binary(input_format_t::cbor, len, result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { std::uint64_t len{}; return get_number(input_format_t::cbor, len) && get_binary(input_format_t::cbor, len, result); } case 0x5F: // Binary data (indefinite length) { while (get() != 0xFF) { binary_t chunk; if (!get_cbor_binary(chunk)) { return false; } result.insert(result.end(), chunk.begin(), chunk.end()); } return true; } default: { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); } } } /*! @param[in] len the length of the array or std::size_t(-1) for an array of indefinite size @param[in] tag_handler how CBOR tags should be treated @return whether array creation completed */ bool get_cbor_array(const std::size_t len, const cbor_tag_handler_t tag_handler) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) { return false; } if (len != std::size_t(-1)) { for (std::size_t i = 0; i < len; ++i) { if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) { return false; } } } else { while (get() != 0xFF) { if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) { return false; } } } return sax->end_array(); } /*! @param[in] len the length of the object or std::size_t(-1) for an object of indefinite size @param[in] tag_handler how CBOR tags should be treated @return whether object creation completed */ bool get_cbor_object(const std::size_t len, const cbor_tag_handler_t tag_handler) { if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) { return false; } if (len != 0) { string_t key; if (len != std::size_t(-1)) { for (std::size_t i = 0; i < len; ++i) { get(); if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) { return false; } if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) { return false; } key.clear(); } } else { while (get() != 0xFF) { if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) { return false; } if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) { return false; } key.clear(); } } } return sax->end_object(); } ///////////// // MsgPack // ///////////// /*! @return whether a valid MessagePack value was passed to the SAX parser */ bool parse_msgpack_internal() { switch (get()) { // EOF case std::char_traits::eof(): return unexpect_eof(input_format_t::msgpack, "value"); // positive fixint case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: return sax->number_unsigned(static_cast(current)); // fixmap case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F: return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); // fixarray case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F: return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); // fixstr case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7: case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF: case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF: case 0xD9: // str 8 case 0xDA: // str 16 case 0xDB: // str 32 { string_t s; return get_msgpack_string(s) && sax->string(s); } case 0xC0: // nil return sax->null(); case 0xC2: // false return sax->boolean(false); case 0xC3: // true return sax->boolean(true); case 0xC4: // bin 8 case 0xC5: // bin 16 case 0xC6: // bin 32 case 0xC7: // ext 8 case 0xC8: // ext 16 case 0xC9: // ext 32 case 0xD4: // fixext 1 case 0xD5: // fixext 2 case 0xD6: // fixext 4 case 0xD7: // fixext 8 case 0xD8: // fixext 16 { binary_t b; return get_msgpack_binary(b) && sax->binary(b); } case 0xCA: // float 32 { float number{}; return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); } case 0xCB: // float 64 { double number{}; return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); } case 0xCC: // uint 8 { std::uint8_t number{}; return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); } case 0xCD: // uint 16 { std::uint16_t number{}; return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); } case 0xCE: // uint 32 { std::uint32_t number{}; return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); } case 0xCF: // uint 64 { std::uint64_t number{}; return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); } case 0xD0: // int 8 { std::int8_t number{}; return get_number(input_format_t::msgpack, number) && sax->number_integer(number); } case 0xD1: // int 16 { std::int16_t number{}; return get_number(input_format_t::msgpack, number) && sax->number_integer(number); } case 0xD2: // int 32 { std::int32_t number{}; return get_number(input_format_t::msgpack, number) && sax->number_integer(number); } case 0xD3: // int 64 { std::int64_t number{}; return get_number(input_format_t::msgpack, number) && sax->number_integer(number); } case 0xDC: // array 16 { std::uint16_t len{}; return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); } case 0xDD: // array 32 { std::uint32_t len{}; return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); } case 0xDE: // map 16 { std::uint16_t len{}; return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); } case 0xDF: // map 32 { std::uint32_t len{}; return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); } // negative fixint case 0xE0: case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7: case 0xE8: case 0xE9: case 0xEA: case 0xEB: case 0xEC: case 0xED: case 0xEE: case 0xEF: case 0xF0: case 0xF1: case 0xF2: case 0xF3: case 0xF4: case 0xF5: case 0xF6: case 0xF7: case 0xF8: case 0xF9: case 0xFA: case 0xFB: case 0xFC: case 0xFD: case 0xFE: case 0xFF: return sax->number_integer(static_cast(current)); default: // anything else { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } /*! @brief reads a MessagePack string This function first reads starting bytes to determine the expected string length and then copies this number of bytes into a string. @param[out] result created string @return whether string creation completed */ bool get_msgpack_string(string_t& result) { if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) { return false; } switch (current) { // fixstr case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7: case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF: case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF: { return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); } case 0xD9: // str 8 { std::uint8_t len{}; return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); } case 0xDA: // str 16 { std::uint16_t len{}; return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); } case 0xDB: // str 32 { std::uint32_t len{}; return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); } default: { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); } } } /*! @brief reads a MessagePack byte array This function first reads starting bytes to determine the expected byte array length and then copies this number of bytes into a byte array. @param[out] result created byte array @return whether byte array creation completed */ bool get_msgpack_binary(binary_t& result) { // helper function to set the subtype auto assign_and_return_true = [&result](std::int8_t subtype) { result.set_subtype(static_cast(subtype)); return true; }; switch (current) { case 0xC4: // bin 8 { std::uint8_t len{}; return get_number(input_format_t::msgpack, len) && get_binary(input_format_t::msgpack, len, result); } case 0xC5: // bin 16 { std::uint16_t len{}; return get_number(input_format_t::msgpack, len) && get_binary(input_format_t::msgpack, len, result); } case 0xC6: // bin 32 { std::uint32_t len{}; return get_number(input_format_t::msgpack, len) && get_binary(input_format_t::msgpack, len, result); } case 0xC7: // ext 8 { std::uint8_t len{}; std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) && get_number(input_format_t::msgpack, subtype) && get_binary(input_format_t::msgpack, len, result) && assign_and_return_true(subtype); } case 0xC8: // ext 16 { std::uint16_t len{}; std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) && get_number(input_format_t::msgpack, subtype) && get_binary(input_format_t::msgpack, len, result) && assign_and_return_true(subtype); } case 0xC9: // ext 32 { std::uint32_t len{}; std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) && get_number(input_format_t::msgpack, subtype) && get_binary(input_format_t::msgpack, len, result) && assign_and_return_true(subtype); } case 0xD4: // fixext 1 { std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) && get_binary(input_format_t::msgpack, 1, result) && assign_and_return_true(subtype); } case 0xD5: // fixext 2 { std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) && get_binary(input_format_t::msgpack, 2, result) && assign_and_return_true(subtype); } case 0xD6: // fixext 4 { std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) && get_binary(input_format_t::msgpack, 4, result) && assign_and_return_true(subtype); } case 0xD7: // fixext 8 { std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) && get_binary(input_format_t::msgpack, 8, result) && assign_and_return_true(subtype); } case 0xD8: // fixext 16 { std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) && get_binary(input_format_t::msgpack, 16, result) && assign_and_return_true(subtype); } default: // LCOV_EXCL_LINE return false; // LCOV_EXCL_LINE } } /*! @param[in] len the length of the array @return whether array creation completed */ bool get_msgpack_array(const std::size_t len) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) { return false; } for (std::size_t i = 0; i < len; ++i) { if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) { return false; } } return sax->end_array(); } /*! @param[in] len the length of the object @return whether object creation completed */ bool get_msgpack_object(const std::size_t len) { if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) { return false; } string_t key; for (std::size_t i = 0; i < len; ++i) { get(); if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) { return false; } if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) { return false; } key.clear(); } return sax->end_object(); } //////////// // UBJSON // //////////// /*! @param[in] get_char whether a new character should be retrieved from the input (true, default) or whether the last read character should be considered instead @return whether a valid UBJSON value was passed to the SAX parser */ bool parse_ubjson_internal(const bool get_char = true) { return get_ubjson_value(get_char ? get_ignore_noop() : current); } /*! @brief reads a UBJSON string This function is either called after reading the 'S' byte explicitly indicating a string, or in case of an object key where the 'S' byte can be left out. @param[out] result created string @param[in] get_char whether a new character should be retrieved from the input (true, default) or whether the last read character should be considered instead @return whether string creation completed */ bool get_ubjson_string(string_t& result, const bool get_char = true) { if (get_char) { get(); // TODO(niels): may we ignore N here? } if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) { return false; } switch (current) { case 'U': { std::uint8_t len{}; return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } case 'i': { std::int8_t len{}; return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } case 'I': { std::int16_t len{}; return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } case 'l': { std::int32_t len{}; return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } case 'L': { std::int64_t len{}; return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } default: auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); } } /*! @param[out] result determined size @return whether size determination completed */ bool get_ubjson_size_value(std::size_t& result) { switch (get_ignore_noop()) { case 'U': { std::uint8_t number{}; if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } result = static_cast(number); return true; } case 'i': { std::int8_t number{}; if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } result = static_cast(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char return true; } case 'I': { std::int16_t number{}; if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } result = static_cast(number); return true; } case 'l': { std::int32_t number{}; if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } result = static_cast(number); return true; } case 'L': { std::int64_t number{}; if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } result = static_cast(number); return true; } default: { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); } } } /*! @brief determine the type and size for a container In the optimized UBJSON format, a type and a size can be provided to allow for a more compact representation. @param[out] result pair of the size and the type @return whether pair creation completed */ bool get_ubjson_size_type(std::pair& result) { result.first = string_t::npos; // size result.second = 0; // type get_ignore_noop(); if (current == '$') { result.second = get(); // must not ignore 'N', because 'N' maybe the type if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) { return false; } get_ignore_noop(); if (JSON_HEDLEY_UNLIKELY(current != '#')) { if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) { return false; } auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); } return get_ubjson_size_value(result.first); } if (current == '#') { return get_ubjson_size_value(result.first); } return true; } /*! @param prefix the previously read or set type prefix @return whether value creation completed */ bool get_ubjson_value(const char_int_type prefix) { switch (prefix) { case std::char_traits::eof(): // EOF return unexpect_eof(input_format_t::ubjson, "value"); case 'T': // true return sax->boolean(true); case 'F': // false return sax->boolean(false); case 'Z': // null return sax->null(); case 'U': { std::uint8_t number{}; return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); } case 'i': { std::int8_t number{}; return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } case 'I': { std::int16_t number{}; return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } case 'l': { std::int32_t number{}; return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } case 'L': { std::int64_t number{}; return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } case 'd': { float number{}; return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); } case 'D': { double number{}; return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); } case 'H': { return get_ubjson_high_precision_number(); } case 'C': // char { get(); if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) { return false; } if (JSON_HEDLEY_UNLIKELY(current > 127)) { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); } string_t s(1, static_cast(current)); return sax->string(s); } case 'S': // string { string_t s; return get_ubjson_string(s) && sax->string(s); } case '[': // array return get_ubjson_array(); case '{': // object return get_ubjson_object(); default: // anything else { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } /*! @return whether array creation completed */ bool get_ubjson_array() { std::pair size_and_type; if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) { return false; } if (size_and_type.first != string_t::npos) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) { return false; } if (size_and_type.second != 0) { if (size_and_type.second != 'N') { for (std::size_t i = 0; i < size_and_type.first; ++i) { if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) { return false; } } } } else { for (std::size_t i = 0; i < size_and_type.first; ++i) { if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) { return false; } } } } else { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) { return false; } while (current != ']') { if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) { return false; } get_ignore_noop(); } } return sax->end_array(); } /*! @return whether object creation completed */ bool get_ubjson_object() { std::pair size_and_type; if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) { return false; } string_t key; if (size_and_type.first != string_t::npos) { if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) { return false; } if (size_and_type.second != 0) { for (std::size_t i = 0; i < size_and_type.first; ++i) { if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) { return false; } if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) { return false; } key.clear(); } } else { for (std::size_t i = 0; i < size_and_type.first; ++i) { if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) { return false; } if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) { return false; } key.clear(); } } } else { if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) { return false; } while (current != '}') { if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) { return false; } if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) { return false; } get_ignore_noop(); key.clear(); } } return sax->end_object(); } // Note, no reader for UBJSON binary types is implemented because they do // not exist bool get_ubjson_high_precision_number() { // get size of following number string std::size_t size{}; auto res = get_ubjson_size_value(size); if (JSON_HEDLEY_UNLIKELY(!res)) { return res; } // get number string std::vector number_vector; for (std::size_t i = 0; i < size; ++i) { get(); if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) { return false; } number_vector.push_back(static_cast(current)); } // parse number string using ia_type = decltype(detail::input_adapter(number_vector)); auto number_lexer = detail::lexer(detail::input_adapter(number_vector), false); const auto result_number = number_lexer.scan(); const auto number_string = number_lexer.get_token_string(); const auto result_remainder = number_lexer.scan(); using token_type = typename detail::lexer_base::token_type; if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); } switch (result_number) { case token_type::value_integer: return sax->number_integer(number_lexer.get_number_integer()); case token_type::value_unsigned: return sax->number_unsigned(number_lexer.get_number_unsigned()); case token_type::value_float: return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); case token_type::uninitialized: case token_type::literal_true: case token_type::literal_false: case token_type::literal_null: case token_type::value_string: case token_type::begin_array: case token_type::begin_object: case token_type::end_array: case token_type::end_object: case token_type::name_separator: case token_type::value_separator: case token_type::parse_error: case token_type::end_of_input: case token_type::literal_or_value: default: return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); } } /////////////////////// // Utility functions // /////////////////////// /*! @brief get next character from the input This function provides the interface to the used input adapter. It does not throw in case the input reached EOF, but returns a -'ve valued `std::char_traits::eof()` in that case. @return character read from the input */ char_int_type get() { ++chars_read; return current = ia.get_character(); } /*! @return character read from the input after ignoring all 'N' entries */ char_int_type get_ignore_noop() { do { get(); } while (current == 'N'); return current; } /* @brief read a number from the input @tparam NumberType the type of the number @param[in] format the current format (for diagnostics) @param[out] result number of type @a NumberType @return whether conversion completed @note This function needs to respect the system's endianess, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ template bool get_number(const input_format_t format, NumberType& result) { // step 1: read input into array with system's byte order std::array vec{}; for (std::size_t i = 0; i < sizeof(NumberType); ++i) { get(); if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) { return false; } // reverse byte order prior to conversion if necessary if (is_little_endian != InputIsLittleEndian) { vec[sizeof(NumberType) - i - 1] = static_cast(current); } else { vec[i] = static_cast(current); // LCOV_EXCL_LINE } } // step 2: convert array into number of type T and return std::memcpy(&result, vec.data(), sizeof(NumberType)); return true; } /*! @brief create a string by reading characters from the input @tparam NumberType the type of the number @param[in] format the current format (for diagnostics) @param[in] len number of characters to read @param[out] result string created by reading @a len bytes @return whether string creation completed @note We can not reserve @a len bytes for the result, because @a len may be too large. Usually, @ref unexpect_eof() detects the end of the input before we run out of string memory. */ template bool get_string(const input_format_t format, const NumberType len, string_t& result) { bool success = true; for (NumberType i = 0; i < len; i++) { get(); if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) { success = false; break; } result.push_back(static_cast(current)); } return success; } /*! @brief create a byte array by reading bytes from the input @tparam NumberType the type of the number @param[in] format the current format (for diagnostics) @param[in] len number of bytes to read @param[out] result byte array created by reading @a len bytes @return whether byte array creation completed @note We can not reserve @a len bytes for the result, because @a len may be too large. Usually, @ref unexpect_eof() detects the end of the input before we run out of memory. */ template bool get_binary(const input_format_t format, const NumberType len, binary_t& result) { bool success = true; for (NumberType i = 0; i < len; i++) { get(); if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) { success = false; break; } result.push_back(static_cast(current)); } return success; } /*! @param[in] format the current format (for diagnostics) @param[in] context further context information (for diagnostics) @return whether the last read character is not EOF */ JSON_HEDLEY_NON_NULL(3) bool unexpect_eof(const input_format_t format, const char* context) const { if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { return sax->parse_error(chars_read, "", parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); } return true; } /*! @return a string representation of the last read byte */ std::string get_token_string() const { std::array cr{{}}; (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) return std::string{cr.data()}; } /*! @param[in] format the current format @param[in] detail a detailed error message @param[in] context further context information @return a message string to use in the parse_error exceptions */ std::string exception_message(const input_format_t format, const std::string& detail, const std::string& context) const { std::string error_msg = "syntax error while parsing "; switch (format) { case input_format_t::cbor: error_msg += "CBOR"; break; case input_format_t::msgpack: error_msg += "MessagePack"; break; case input_format_t::ubjson: error_msg += "UBJSON"; break; case input_format_t::bson: error_msg += "BSON"; break; case input_format_t::json: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } return error_msg + " " + context + ": " + detail; } private: /// input adapter InputAdapterType ia; /// the current character char_int_type current = std::char_traits::eof(); /// the number of characters read std::size_t chars_read = 0; /// whether we can assume little endianess const bool is_little_endian = little_endianess(); /// the SAX parser json_sax_t* sax = nullptr; }; } // namespace detail } // namespace nlohmann // #include // #include // #include #include // isfinite #include // uint8_t #include // function #include // string #include // move #include // vector // #include // #include // #include // #include // #include // #include // #include namespace nlohmann { namespace detail { //////////// // parser // //////////// enum class parse_event_t : std::uint8_t { /// the parser read `{` and started to process a JSON object object_start, /// the parser read `}` and finished processing a JSON object object_end, /// the parser read `[` and started to process a JSON array array_start, /// the parser read `]` and finished processing a JSON array array_end, /// the parser read a key of a value in an object key, /// the parser finished reading a JSON value value }; template using parser_callback_t = std::function; /*! @brief syntax analysis This class implements a recursive descent parser. */ template class parser { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using lexer_t = lexer; using token_type = typename lexer_t::token_type; public: /// a parser reading from an input adapter explicit parser(InputAdapterType&& adapter, const parser_callback_t cb = nullptr, const bool allow_exceptions_ = true, const bool skip_comments = false) : callback(cb) , m_lexer(std::move(adapter), skip_comments) , allow_exceptions(allow_exceptions_) { // read first token get_token(); } /*! @brief public parser interface @param[in] strict whether to expect the last token to be EOF @param[in,out] result parsed JSON value @throw parse_error.101 in case of an unexpected token @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails */ void parse(const bool strict, BasicJsonType& result) { if (callback) { json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); sax_parse_internal(&sdp); // in strict mode, input must be completely read if (strict && (get_token() != token_type::end_of_input)) { sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); } // in case of an error, return discarded value if (sdp.is_errored()) { result = value_t::discarded; return; } // set top-level value to null if it was discarded by the callback // function if (result.is_discarded()) { result = nullptr; } } else { json_sax_dom_parser sdp(result, allow_exceptions); sax_parse_internal(&sdp); // in strict mode, input must be completely read if (strict && (get_token() != token_type::end_of_input)) { sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); } // in case of an error, return discarded value if (sdp.is_errored()) { result = value_t::discarded; return; } } result.assert_invariant(); } /*! @brief public accept interface @param[in] strict whether to expect the last token to be EOF @return whether the input is a proper JSON text */ bool accept(const bool strict = true) { json_sax_acceptor sax_acceptor; return sax_parse(&sax_acceptor, strict); } template JSON_HEDLEY_NON_NULL(2) bool sax_parse(SAX* sax, const bool strict = true) { (void)detail::is_sax_static_asserts {}; const bool result = sax_parse_internal(sax); // strict mode: next byte must be EOF if (result && strict && (get_token() != token_type::end_of_input)) { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); } return result; } private: template JSON_HEDLEY_NON_NULL(2) bool sax_parse_internal(SAX* sax) { // stack to remember the hierarchy of structured values we are parsing // true = array; false = object std::vector states; // value to avoid a goto (see comment where set to true) bool skip_to_state_evaluation = false; while (true) { if (!skip_to_state_evaluation) { // invariant: get_token() was called before each iteration switch (last_token) { case token_type::begin_object: { if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) { return false; } // closing } -> we are done if (get_token() == token_type::end_object) { if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) { return false; } break; } // parse key if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { return false; } // parse separator (:) if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); } // remember we are now inside an object states.push_back(false); // parse values get_token(); continue; } case token_type::begin_array: { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) { return false; } // closing ] -> we are done if (get_token() == token_type::end_array) { if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) { return false; } break; } // remember we are now inside an array states.push_back(true); // parse values (no need to call get_token) continue; } case token_type::value_float: { const auto res = m_lexer.get_number_float(); if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); } if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) { return false; } break; } case token_type::literal_false: { if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) { return false; } break; } case token_type::literal_null: { if (JSON_HEDLEY_UNLIKELY(!sax->null())) { return false; } break; } case token_type::literal_true: { if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) { return false; } break; } case token_type::value_integer: { if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) { return false; } break; } case token_type::value_string: { if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) { return false; } break; } case token_type::value_unsigned: { if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) { return false; } break; } case token_type::parse_error: { // using "uninitialized" to avoid "expected" message return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); } case token_type::uninitialized: case token_type::end_array: case token_type::end_object: case token_type::name_separator: case token_type::value_separator: case token_type::end_of_input: case token_type::literal_or_value: default: // the last token was unexpected { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); } } } else { skip_to_state_evaluation = false; } // we reached this line after we successfully parsed a value if (states.empty()) { // empty stack: we reached the end of the hierarchy: done return true; } if (states.back()) // array { // comma -> next value if (get_token() == token_type::value_separator) { // parse a new value get_token(); continue; } // closing ] if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) { if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) { return false; } // We are done with this array. Before we can parse a // new value, we need to evaluate the new state first. // By setting skip_to_state_evaluation to false, we // are effectively jumping to the beginning of this if. JSON_ASSERT(!states.empty()); states.pop_back(); skip_to_state_evaluation = true; continue; } return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); } // states.back() is false -> object // comma -> next value if (get_token() == token_type::value_separator) { // parse key if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { return false; } // parse separator (:) if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); } // parse values get_token(); continue; } // closing } if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) { if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) { return false; } // We are done with this object. Before we can parse a // new value, we need to evaluate the new state first. // By setting skip_to_state_evaluation to false, we // are effectively jumping to the beginning of this if. JSON_ASSERT(!states.empty()); states.pop_back(); skip_to_state_evaluation = true; continue; } return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); } } /// get next token from lexer token_type get_token() { return last_token = m_lexer.scan(); } std::string exception_message(const token_type expected, const std::string& context) { std::string error_msg = "syntax error "; if (!context.empty()) { error_msg += "while parsing " + context + " "; } error_msg += "- "; if (last_token == token_type::parse_error) { error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + m_lexer.get_token_string() + "'"; } else { error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); } if (expected != token_type::uninitialized) { error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); } return error_msg; } private: /// callback function const parser_callback_t callback = nullptr; /// the type of the last read token token_type last_token = token_type::uninitialized; /// the lexer lexer_t m_lexer; /// whether to throw exceptions in case of errors const bool allow_exceptions = true; }; } // namespace detail } // namespace nlohmann // #include // #include #include // ptrdiff_t #include // numeric_limits // #include namespace nlohmann { namespace detail { /* @brief an iterator for primitive JSON types This class models an iterator for primitive JSON types (boolean, number, string). It's only purpose is to allow the iterator/const_iterator classes to "iterate" over primitive values. Internally, the iterator is modeled by a `difference_type` variable. Value begin_value (`0`) models the begin, end_value (`1`) models past the end. */ class primitive_iterator_t { private: using difference_type = std::ptrdiff_t; static constexpr difference_type begin_value = 0; static constexpr difference_type end_value = begin_value + 1; JSON_PRIVATE_UNLESS_TESTED: /// iterator as signed integer type difference_type m_it = (std::numeric_limits::min)(); public: constexpr difference_type get_value() const noexcept { return m_it; } /// set iterator to a defined beginning void set_begin() noexcept { m_it = begin_value; } /// set iterator to a defined past the end void set_end() noexcept { m_it = end_value; } /// return whether the iterator can be dereferenced constexpr bool is_begin() const noexcept { return m_it == begin_value; } /// return whether the iterator is at end constexpr bool is_end() const noexcept { return m_it == end_value; } friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it == rhs.m_it; } friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it < rhs.m_it; } primitive_iterator_t operator+(difference_type n) noexcept { auto result = *this; result += n; return result; } friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it - rhs.m_it; } primitive_iterator_t& operator++() noexcept { ++m_it; return *this; } primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) { auto result = *this; ++m_it; return result; } primitive_iterator_t& operator--() noexcept { --m_it; return *this; } primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) { auto result = *this; --m_it; return result; } primitive_iterator_t& operator+=(difference_type n) noexcept { m_it += n; return *this; } primitive_iterator_t& operator-=(difference_type n) noexcept { m_it -= n; return *this; } }; } // namespace detail } // namespace nlohmann namespace nlohmann { namespace detail { /*! @brief an iterator value @note This structure could easily be a union, but MSVC currently does not allow unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. */ template struct internal_iterator { /// iterator for JSON objects typename BasicJsonType::object_t::iterator object_iterator {}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; } // namespace detail } // namespace nlohmann // #include #include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include // conditional, is_const, remove_const // #include // #include // #include // #include // #include // #include // #include namespace nlohmann { namespace detail { // forward declare, to be able to friend it later on template class iteration_proxy; template class iteration_proxy_value; /*! @brief a template for a bidirectional iterator for the @ref basic_json class This class implements a both iterators (iterator and const_iterator) for the @ref basic_json class. @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the iterator is default-constructed, it is *uninitialized* and most methods are undefined. **The library uses assertions to detect calls on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): The iterator that can be moved can be moved in both directions (i.e. incremented and decremented). @since version 1.0.0, simplified in version 2.0.9, change to bidirectional iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) */ template class iter_impl { /// the iterator with BasicJsonType of different const-ness using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; /// allow basic_json to access private members friend other_iter_impl; friend BasicJsonType; friend iteration_proxy; friend iteration_proxy_value; using object_t = typename BasicJsonType::object_t; using array_t = typename BasicJsonType::array_t; // make sure BasicJsonType is basic_json or const basic_json static_assert(is_basic_json::type>::value, "iter_impl only accepts (const) basic_json"); public: /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. /// The C++ Standard has never required user-defined iterators to derive from std::iterator. /// A user-defined iterator should provide publicly accessible typedefs named /// iterator_category, value_type, difference_type, pointer, and reference. /// Note that value_type is required to be non-const, even for constant iterators. using iterator_category = std::bidirectional_iterator_tag; /// the type of the values when the iterator is dereferenced using value_type = typename BasicJsonType::value_type; /// a type to represent differences between iterators using difference_type = typename BasicJsonType::difference_type; /// defines a pointer to the type iterated over (value_type) using pointer = typename std::conditional::value, typename BasicJsonType::const_pointer, typename BasicJsonType::pointer>::type; /// defines a reference to the type iterated over (value_type) using reference = typename std::conditional::value, typename BasicJsonType::const_reference, typename BasicJsonType::reference>::type; iter_impl() = default; ~iter_impl() = default; iter_impl(iter_impl&&) noexcept = default; iter_impl& operator=(iter_impl&&) noexcept = default; /*! @brief constructor for a given JSON instance @param[in] object pointer to a JSON object for this iterator @pre object != nullptr @post The iterator is initialized; i.e. `m_object != nullptr`. */ explicit iter_impl(pointer object) noexcept : m_object(object) { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { m_it.object_iterator = typename object_t::iterator(); break; } case value_t::array: { m_it.array_iterator = typename array_t::iterator(); break; } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { m_it.primitive_iterator = primitive_iterator_t(); break; } } } /*! @note The conventional copy constructor and copy assignment are implicitly defined. Combined with the following converting constructor and assignment, they support: (1) copy from iterator to iterator, (2) copy from const iterator to const iterator, and (3) conversion from iterator to const iterator. However conversion from const iterator to iterator is not defined. */ /*! @brief const copy constructor @param[in] other const iterator to copy from @note This copy constructor had to be defined explicitly to circumvent a bug occurring on msvc v19.0 compiler (VS 2015) debug build. For more information refer to: https://github.com/nlohmann/json/issues/1608 */ iter_impl(const iter_impl& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} /*! @brief converting assignment @param[in] other const iterator to copy from @return const/non-const iterator @note It is not checked whether @a other is initialized. */ iter_impl& operator=(const iter_impl& other) noexcept { if (&other != this) { m_object = other.m_object; m_it = other.m_it; } return *this; } /*! @brief converting constructor @param[in] other non-const iterator to copy from @note It is not checked whether @a other is initialized. */ iter_impl(const iter_impl::type>& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} /*! @brief converting assignment @param[in] other non-const iterator to copy from @return const/non-const iterator @note It is not checked whether @a other is initialized. */ iter_impl& operator=(const iter_impl::type>& other) noexcept // NOLINT(cert-oop54-cpp) { m_object = other.m_object; m_it = other.m_it; return *this; } JSON_PRIVATE_UNLESS_TESTED: /*! @brief set the iterator to the first value @pre The iterator is initialized; i.e. `m_object != nullptr`. */ void set_begin() noexcept { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { m_it.object_iterator = m_object->m_value.object->begin(); break; } case value_t::array: { m_it.array_iterator = m_object->m_value.array->begin(); break; } case value_t::null: { // set to end so begin()==end() is true: null is empty m_it.primitive_iterator.set_end(); break; } case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { m_it.primitive_iterator.set_begin(); break; } } } /*! @brief set the iterator past the last value @pre The iterator is initialized; i.e. `m_object != nullptr`. */ void set_end() noexcept { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { m_it.object_iterator = m_object->m_value.object->end(); break; } case value_t::array: { m_it.array_iterator = m_object->m_value.array->end(); break; } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { m_it.primitive_iterator.set_end(); break; } } } public: /*! @brief return a reference to the value pointed to by the iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference operator*() const { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case value_t::array: { JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } case value_t::null: JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) { return *m_object; } JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); } } } /*! @brief dereference the iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ pointer operator->() const { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case value_t::array: { JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) { return m_object; } JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); } } } /*! @brief post-increment (it++) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl const operator++(int) // NOLINT(readability-const-return-type) { auto result = *this; ++(*this); return result; } /*! @brief pre-increment (++it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator++() { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { std::advance(m_it.object_iterator, 1); break; } case value_t::array: { std::advance(m_it.array_iterator, 1); break; } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { ++m_it.primitive_iterator; break; } } return *this; } /*! @brief post-decrement (it--) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl const operator--(int) // NOLINT(readability-const-return-type) { auto result = *this; --(*this); return result; } /*! @brief pre-decrement (--it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator--() { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { std::advance(m_it.object_iterator, -1); break; } case value_t::array: { std::advance(m_it.array_iterator, -1); break; } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { --m_it.primitive_iterator; break; } } return *this; } /*! @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > bool operator==(const IterImpl& other) const { // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); } JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: return (m_it.object_iterator == other.m_it.object_iterator); case value_t::array: return (m_it.array_iterator == other.m_it.array_iterator); case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: return (m_it.primitive_iterator == other.m_it.primitive_iterator); } } /*! @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > bool operator!=(const IterImpl& other) const { return !operator==(other); } /*! @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); } JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: return (m_it.primitive_iterator < other.m_it.primitive_iterator); } } /*! @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator<=(const iter_impl& other) const { return !other.operator < (*this); } /*! @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator>(const iter_impl& other) const { return !operator<=(other); } /*! @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator>=(const iter_impl& other) const { return !operator<(other); } /*! @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator+=(difference_type i) { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); case value_t::array: { std::advance(m_it.array_iterator, i); break; } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { m_it.primitive_iterator += i; break; } } return *this; } /*! @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator-=(difference_type i) { return operator+=(-i); } /*! @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl operator+(difference_type i) const { auto result = *this; result += i; return result; } /*! @brief addition of distance and iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ friend iter_impl operator+(difference_type i, const iter_impl& it) { auto result = it; result += i; return result; } /*! @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl operator-(difference_type i) const { auto result = *this; result -= i; return result; } /*! @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ difference_type operator-(const iter_impl& other) const { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: return m_it.primitive_iterator - other.m_it.primitive_iterator; } } /*! @brief access to successor @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference operator[](difference_type n) const { JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) { return *m_object; } JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); } } } /*! @brief return the key of an object iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ const typename object_t::key_type& key() const { JSON_ASSERT(m_object != nullptr); if (JSON_HEDLEY_LIKELY(m_object->is_object())) { return m_it.object_iterator->first; } JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); } /*! @brief return the value of an iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference value() const { return operator*(); } JSON_PRIVATE_UNLESS_TESTED: /// associated JSON instance pointer m_object = nullptr; /// the actual iterator of the associated instance internal_iterator::type> m_it {}; }; } // namespace detail } // namespace nlohmann // #include // #include #include // ptrdiff_t #include // reverse_iterator #include // declval namespace nlohmann { namespace detail { ////////////////////// // reverse_iterator // ////////////////////// /*! @brief a template for a reverse iterator class @tparam Base the base iterator type to reverse. Valid types are @ref iterator (to create @ref reverse_iterator) and @ref const_iterator (to create @ref const_reverse_iterator). @requirement The class satisfies the following concept requirements: - [BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): The iterator that can be moved can be moved in both directions (i.e. incremented and decremented). - [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): It is possible to write to the pointed-to element (only if @a Base is @ref iterator). @since version 1.0.0 */ template class json_reverse_iterator : public std::reverse_iterator { public: using difference_type = std::ptrdiff_t; /// shortcut to the reverse iterator adapter using base_iterator = std::reverse_iterator; /// the reference type for the pointed-to element using reference = typename Base::reference; /// create reverse iterator from iterator explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept : base_iterator(it) {} /// create reverse iterator from base class explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} /// post-increment (it++) json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) { return static_cast(base_iterator::operator++(1)); } /// pre-increment (++it) json_reverse_iterator& operator++() { return static_cast(base_iterator::operator++()); } /// post-decrement (it--) json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) { return static_cast(base_iterator::operator--(1)); } /// pre-decrement (--it) json_reverse_iterator& operator--() { return static_cast(base_iterator::operator--()); } /// add to iterator json_reverse_iterator& operator+=(difference_type i) { return static_cast(base_iterator::operator+=(i)); } /// add to iterator json_reverse_iterator operator+(difference_type i) const { return static_cast(base_iterator::operator+(i)); } /// subtract from iterator json_reverse_iterator operator-(difference_type i) const { return static_cast(base_iterator::operator-(i)); } /// return difference difference_type operator-(const json_reverse_iterator& other) const { return base_iterator(*this) - base_iterator(other); } /// access to successor reference operator[](difference_type n) const { return *(this->operator+(n)); } /// return the key of an object iterator auto key() const -> decltype(std::declval().key()) { auto it = --this->base(); return it.key(); } /// return the value of an iterator reference value() const { auto it = --this->base(); return it.operator * (); } }; } // namespace detail } // namespace nlohmann // #include // #include #include // all_of #include // isdigit #include // max #include // accumulate #include // string #include // move #include // vector // #include // #include // #include // #include namespace nlohmann { template class json_pointer { // allow basic_json to access private members NLOHMANN_BASIC_JSON_TPL_DECLARATION friend class basic_json; public: /*! @brief create JSON pointer Create a JSON pointer according to the syntax described in [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). @param[in] s string representing the JSON pointer; if omitted, the empty string is assumed which references the whole JSON value @throw parse_error.107 if the given JSON pointer @a s is nonempty and does not begin with a slash (`/`); see example below @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is not followed by `0` (representing `~`) or `1` (representing `/`); see example below @liveexample{The example shows the construction several valid JSON pointers as well as the exceptional behavior.,json_pointer} @since version 2.0.0 */ explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {} /*! @brief return a string representation of the JSON pointer @invariant For each JSON pointer `ptr`, it holds: @code {.cpp} ptr == json_pointer(ptr.to_string()); @endcode @return a string representation of the JSON pointer @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} @since version 2.0.0 */ std::string to_string() const { return std::accumulate(reference_tokens.begin(), reference_tokens.end(), std::string{}, [](const std::string & a, const std::string & b) { return a + "/" + detail::escape(b); }); } /// @copydoc to_string() operator std::string() const { return to_string(); } /*! @brief append another JSON pointer at the end of this JSON pointer @param[in] ptr JSON pointer to append @return JSON pointer with @a ptr appended @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} @complexity Linear in the length of @a ptr. @sa see @ref operator/=(std::string) to append a reference token @sa see @ref operator/=(std::size_t) to append an array index @sa see @ref operator/(const json_pointer&, const json_pointer&) for a binary operator @since version 3.6.0 */ json_pointer& operator/=(const json_pointer& ptr) { reference_tokens.insert(reference_tokens.end(), ptr.reference_tokens.begin(), ptr.reference_tokens.end()); return *this; } /*! @brief append an unescaped reference token at the end of this JSON pointer @param[in] token reference token to append @return JSON pointer with @a token appended without escaping @a token @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} @complexity Amortized constant. @sa see @ref operator/=(const json_pointer&) to append a JSON pointer @sa see @ref operator/=(std::size_t) to append an array index @sa see @ref operator/(const json_pointer&, std::size_t) for a binary operator @since version 3.6.0 */ json_pointer& operator/=(std::string token) { push_back(std::move(token)); return *this; } /*! @brief append an array index at the end of this JSON pointer @param[in] array_idx array index to append @return JSON pointer with @a array_idx appended @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} @complexity Amortized constant. @sa see @ref operator/=(const json_pointer&) to append a JSON pointer @sa see @ref operator/=(std::string) to append a reference token @sa see @ref operator/(const json_pointer&, std::string) for a binary operator @since version 3.6.0 */ json_pointer& operator/=(std::size_t array_idx) { return *this /= std::to_string(array_idx); } /*! @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer @param[in] lhs JSON pointer @param[in] rhs JSON pointer @return a new JSON pointer with @a rhs appended to @a lhs @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} @complexity Linear in the length of @a lhs and @a rhs. @sa see @ref operator/=(const json_pointer&) to append a JSON pointer @since version 3.6.0 */ friend json_pointer operator/(const json_pointer& lhs, const json_pointer& rhs) { return json_pointer(lhs) /= rhs; } /*! @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer @param[in] ptr JSON pointer @param[in] token reference token @return a new JSON pointer with unescaped @a token appended to @a ptr @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} @complexity Linear in the length of @a ptr. @sa see @ref operator/=(std::string) to append a reference token @since version 3.6.0 */ friend json_pointer operator/(const json_pointer& ptr, std::string token) // NOLINT(performance-unnecessary-value-param) { return json_pointer(ptr) /= std::move(token); } /*! @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer @param[in] ptr JSON pointer @param[in] array_idx array index @return a new JSON pointer with @a array_idx appended to @a ptr @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} @complexity Linear in the length of @a ptr. @sa see @ref operator/=(std::size_t) to append an array index @since version 3.6.0 */ friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx) { return json_pointer(ptr) /= array_idx; } /*! @brief returns the parent of this JSON pointer @return parent of this JSON pointer; in case this JSON pointer is the root, the root itself is returned @complexity Linear in the length of the JSON pointer. @liveexample{The example shows the result of `parent_pointer` for different JSON Pointers.,json_pointer__parent_pointer} @since version 3.6.0 */ json_pointer parent_pointer() const { if (empty()) { return *this; } json_pointer res = *this; res.pop_back(); return res; } /*! @brief remove last reference token @pre not `empty()` @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} @complexity Constant. @throw out_of_range.405 if JSON pointer has no parent @since version 3.6.0 */ void pop_back() { if (JSON_HEDLEY_UNLIKELY(empty())) { JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); } reference_tokens.pop_back(); } /*! @brief return last reference token @pre not `empty()` @return last reference token @liveexample{The example shows the usage of `back`.,json_pointer__back} @complexity Constant. @throw out_of_range.405 if JSON pointer has no parent @since version 3.6.0 */ const std::string& back() const { if (JSON_HEDLEY_UNLIKELY(empty())) { JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); } return reference_tokens.back(); } /*! @brief append an unescaped token at the end of the reference pointer @param[in] token token to add @complexity Amortized constant. @liveexample{The example shows the result of `push_back` for different JSON Pointers.,json_pointer__push_back} @since version 3.6.0 */ void push_back(const std::string& token) { reference_tokens.push_back(token); } /// @copydoc push_back(const std::string&) void push_back(std::string&& token) { reference_tokens.push_back(std::move(token)); } /*! @brief return whether pointer points to the root document @return true iff the JSON pointer points to the root document @complexity Constant. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example shows the result of `empty` for different JSON Pointers.,json_pointer__empty} @since version 3.6.0 */ bool empty() const noexcept { return reference_tokens.empty(); } private: /*! @param[in] s reference token to be converted into an array index @return integer representation of @a s @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer @throw out_of_range.410 if an array index exceeds size_type */ static typename BasicJsonType::size_type array_index(const std::string& s) { using size_type = typename BasicJsonType::size_type; // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) { JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); } std::size_t processed_chars = 0; unsigned long long res = 0; // NOLINT(runtime/int) JSON_TRY { res = std::stoull(s, &processed_chars); } JSON_CATCH(std::out_of_range&) { JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); } // check if the string was completely read if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) { JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); } // only triggered on special platforms (like 32bit), see also // https://github.com/nlohmann/json/pull/2203 if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) { JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE } return static_cast(res); } JSON_PRIVATE_UNLESS_TESTED: json_pointer top() const { if (JSON_HEDLEY_UNLIKELY(empty())) { JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); } json_pointer result = *this; result.reference_tokens = {reference_tokens[0]}; return result; } private: /*! @brief create and return a reference to the pointed to value @complexity Linear in the number of reference tokens. @throw parse_error.109 if array index is not a number @throw type_error.313 if value cannot be unflattened */ BasicJsonType& get_and_create(BasicJsonType& j) const { auto* result = &j; // in case no reference tokens exist, return a reference to the JSON value // j which will be overwritten by a primitive value for (const auto& reference_token : reference_tokens) { switch (result->type()) { case detail::value_t::null: { if (reference_token == "0") { // start a new array if reference token is 0 result = &result->operator[](0); } else { // start a new object otherwise result = &result->operator[](reference_token); } break; } case detail::value_t::object: { // create an entry in the object result = &result->operator[](reference_token); break; } case detail::value_t::array: { // create an entry in the array result = &result->operator[](array_index(reference_token)); break; } /* The following code is only reached if there exists a reference token _and_ the current value is primitive. In this case, we have an error situation, because primitive values may only occur as single value; that is, with an empty list of reference tokens. */ case detail::value_t::string: case detail::value_t::boolean: case detail::value_t::number_integer: case detail::value_t::number_unsigned: case detail::value_t::number_float: case detail::value_t::binary: case detail::value_t::discarded: default: JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); } } return *result; } /*! @brief return a reference to the pointed to value @note This version does not throw if a value is not present, but tries to create nested values instead. For instance, calling this function with pointer `"/this/that"` on a null value is equivalent to calling `operator[]("this").operator[]("that")` on that value, effectively changing the null value to an object. @param[in] ptr a JSON value @return reference to the JSON value pointed to by the JSON pointer @complexity Linear in the length of the JSON pointer. @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.404 if the JSON pointer can not be resolved */ BasicJsonType& get_unchecked(BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) { // convert null values to arrays or objects before continuing if (ptr->is_null()) { // check if reference token is a number const bool nums = std::all_of(reference_token.begin(), reference_token.end(), [](const unsigned char x) { return std::isdigit(x); }); // change value to array for numbers or "-" or to object otherwise *ptr = (nums || reference_token == "-") ? detail::value_t::array : detail::value_t::object; } switch (ptr->type()) { case detail::value_t::object: { // use unchecked object access ptr = &ptr->operator[](reference_token); break; } case detail::value_t::array: { if (reference_token == "-") { // explicitly treat "-" as index beyond the end ptr = &ptr->operator[](ptr->m_value.array->size()); } else { // convert array index to number; unchecked access ptr = &ptr->operator[](array_index(reference_token)); } break; } case detail::value_t::null: case detail::value_t::string: case detail::value_t::boolean: case detail::value_t::number_integer: case detail::value_t::number_unsigned: case detail::value_t::number_float: case detail::value_t::binary: case detail::value_t::discarded: default: JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); } } return *ptr; } /*! @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ BasicJsonType& get_checked(BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) { switch (ptr->type()) { case detail::value_t::object: { // note: at performs range check ptr = &ptr->at(reference_token); break; } case detail::value_t::array: { if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); } // note: at performs range check ptr = &ptr->at(array_index(reference_token)); break; } case detail::value_t::null: case detail::value_t::string: case detail::value_t::boolean: case detail::value_t::number_integer: case detail::value_t::number_unsigned: case detail::value_t::number_float: case detail::value_t::binary: case detail::value_t::discarded: default: JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); } } return *ptr; } /*! @brief return a const reference to the pointed to value @param[in] ptr a JSON value @return const reference to the JSON value pointed to by the JSON pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) { switch (ptr->type()) { case detail::value_t::object: { // use unchecked object access ptr = &ptr->operator[](reference_token); break; } case detail::value_t::array: { if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); } // use unchecked array access ptr = &ptr->operator[](array_index(reference_token)); break; } case detail::value_t::null: case detail::value_t::string: case detail::value_t::boolean: case detail::value_t::number_integer: case detail::value_t::number_unsigned: case detail::value_t::number_float: case detail::value_t::binary: case detail::value_t::discarded: default: JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); } } return *ptr; } /*! @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ const BasicJsonType& get_checked(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) { switch (ptr->type()) { case detail::value_t::object: { // note: at performs range check ptr = &ptr->at(reference_token); break; } case detail::value_t::array: { if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); } // note: at performs range check ptr = &ptr->at(array_index(reference_token)); break; } case detail::value_t::null: case detail::value_t::string: case detail::value_t::boolean: case detail::value_t::number_integer: case detail::value_t::number_unsigned: case detail::value_t::number_float: case detail::value_t::binary: case detail::value_t::discarded: default: JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); } } return *ptr; } /*! @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number */ bool contains(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) { switch (ptr->type()) { case detail::value_t::object: { if (!ptr->contains(reference_token)) { // we did not find the key in the object return false; } ptr = &ptr->operator[](reference_token); break; } case detail::value_t::array: { if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" always fails the range check return false; } if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) { // invalid char return false; } if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) { if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) { // first char should be between '1' and '9' return false; } for (std::size_t i = 1; i < reference_token.size(); i++) { if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) { // other char should be between '0' and '9' return false; } } } const auto idx = array_index(reference_token); if (idx >= ptr->size()) { // index out of range return false; } ptr = &ptr->operator[](idx); break; } case detail::value_t::null: case detail::value_t::string: case detail::value_t::boolean: case detail::value_t::number_integer: case detail::value_t::number_unsigned: case detail::value_t::number_float: case detail::value_t::binary: case detail::value_t::discarded: default: { // we do not expect primitive values if there is still a // reference token to process return false; } } } // no reference token left means we found a primitive value return true; } /*! @brief split the string input to reference tokens @note This function is only called by the json_pointer constructor. All exceptions below are documented there. @throw parse_error.107 if the pointer is not empty or begins with '/' @throw parse_error.108 if character '~' is not followed by '0' or '1' */ static std::vector split(const std::string& reference_string) { std::vector result; // special case: empty reference string -> no reference tokens if (reference_string.empty()) { return result; } // check if nonempty reference string begins with slash if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) { JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); } // extract the reference tokens: // - slash: position of the last read slash (or end of string) // - start: position after the previous slash for ( // search for the first slash after the first character std::size_t slash = reference_string.find_first_of('/', 1), // set the beginning of the first reference token start = 1; // we can stop if start == 0 (if slash == std::string::npos) start != 0; // set the beginning of the next reference token // (will eventually be 0 if slash == std::string::npos) start = (slash == std::string::npos) ? 0 : slash + 1, // find next slash slash = reference_string.find_first_of('/', start)) { // use the text between the beginning of the reference token // (start) and the last slash (slash). auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped for (std::size_t pos = reference_token.find_first_of('~'); pos != std::string::npos; pos = reference_token.find_first_of('~', pos + 1)) { JSON_ASSERT(reference_token[pos] == '~'); // ~ must be followed by 0 or 1 if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || (reference_token[pos + 1] != '0' && reference_token[pos + 1] != '1'))) { JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); } } // finally, store the reference token detail::unescape(reference_token); result.push_back(reference_token); } return result; } private: /*! @param[in] reference_string the reference string to the current value @param[in] value the value to consider @param[in,out] result the result object to insert values to @note Empty objects or arrays are flattened to `null`. */ static void flatten(const std::string& reference_string, const BasicJsonType& value, BasicJsonType& result) { switch (value.type()) { case detail::value_t::array: { if (value.m_value.array->empty()) { // flatten empty array as null result[reference_string] = nullptr; } else { // iterate array and use index as reference string for (std::size_t i = 0; i < value.m_value.array->size(); ++i) { flatten(reference_string + "/" + std::to_string(i), value.m_value.array->operator[](i), result); } } break; } case detail::value_t::object: { if (value.m_value.object->empty()) { // flatten empty object as null result[reference_string] = nullptr; } else { // iterate object and use keys as reference string for (const auto& element : *value.m_value.object) { flatten(reference_string + "/" + detail::escape(element.first), element.second, result); } } break; } case detail::value_t::null: case detail::value_t::string: case detail::value_t::boolean: case detail::value_t::number_integer: case detail::value_t::number_unsigned: case detail::value_t::number_float: case detail::value_t::binary: case detail::value_t::discarded: default: { // add primitive value with its reference string result[reference_string] = value; break; } } } /*! @param[in] value flattened JSON @return unflattened JSON @throw parse_error.109 if array index is not a number @throw type_error.314 if value is not an object @throw type_error.315 if object values are not primitive @throw type_error.313 if value cannot be unflattened */ static BasicJsonType unflatten(const BasicJsonType& value) { if (JSON_HEDLEY_UNLIKELY(!value.is_object())) { JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); } BasicJsonType result; // iterate the JSON object values for (const auto& element : *value.m_value.object) { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); } // assign value to reference pointed to by JSON pointer; Note that if // the JSON pointer is "" (i.e., points to the whole value), function // get_and_create returns a reference to result itself. An assignment // will then create a primitive value. json_pointer(element.first).get_and_create(result) = element.second; } return result; } /*! @brief compares two JSON pointers for equality @param[in] lhs JSON pointer to compare @param[in] rhs JSON pointer to compare @return whether @a lhs is equal to @a rhs @complexity Linear in the length of the JSON pointer @exceptionsafety No-throw guarantee: this function never throws exceptions. */ friend bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept { return lhs.reference_tokens == rhs.reference_tokens; } /*! @brief compares two JSON pointers for inequality @param[in] lhs JSON pointer to compare @param[in] rhs JSON pointer to compare @return whether @a lhs is not equal @a rhs @complexity Linear in the length of the JSON pointer @exceptionsafety No-throw guarantee: this function never throws exceptions. */ friend bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept { return !(lhs == rhs); } /// the reference tokens std::vector reference_tokens; }; } // namespace nlohmann // #include #include #include // #include namespace nlohmann { namespace detail { template class json_ref { public: using value_type = BasicJsonType; json_ref(value_type&& value) : owned_value(std::move(value)) {} json_ref(const value_type& value) : value_ref(&value) {} json_ref(std::initializer_list init) : owned_value(init) {} template < class... Args, enable_if_t::value, int> = 0 > json_ref(Args && ... args) : owned_value(std::forward(args)...) {} // class should be movable only json_ref(json_ref&&) noexcept = default; json_ref(const json_ref&) = delete; json_ref& operator=(const json_ref&) = delete; json_ref& operator=(json_ref&&) = delete; ~json_ref() = default; value_type moved_or_copied() const { if (value_ref == nullptr) { return std::move(owned_value); } return *value_ref; } value_type const& operator*() const { return value_ref ? *value_ref : owned_value; } value_type const* operator->() const { return &** this; } private: mutable value_type owned_value = nullptr; value_type const* value_ref = nullptr; }; } // namespace detail } // namespace nlohmann // #include // #include // #include // #include // #include #include // reverse #include // array #include // isnan, isinf #include // uint8_t, uint16_t, uint32_t, uint64_t #include // memcpy #include // numeric_limits #include // string #include // move // #include // #include // #include #include // copy #include // size_t #include // back_inserter #include // shared_ptr, make_shared #include // basic_string #include // vector #ifndef JSON_NO_IO #include // streamsize #include // basic_ostream #endif // JSON_NO_IO // #include namespace nlohmann { namespace detail { /// abstract output adapter interface template struct output_adapter_protocol { virtual void write_character(CharType c) = 0; virtual void write_characters(const CharType* s, std::size_t length) = 0; virtual ~output_adapter_protocol() = default; output_adapter_protocol() = default; output_adapter_protocol(const output_adapter_protocol&) = default; output_adapter_protocol(output_adapter_protocol&&) noexcept = default; output_adapter_protocol& operator=(const output_adapter_protocol&) = default; output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default; }; /// a type to simplify interfaces template using output_adapter_t = std::shared_ptr>; /// output adapter for byte vectors template> class output_vector_adapter : public output_adapter_protocol { public: explicit output_vector_adapter(std::vector& vec) noexcept : v(vec) {} void write_character(CharType c) override { v.push_back(c); } JSON_HEDLEY_NON_NULL(2) void write_characters(const CharType* s, std::size_t length) override { std::copy(s, s + length, std::back_inserter(v)); } private: std::vector& v; }; #ifndef JSON_NO_IO /// output adapter for output streams template class output_stream_adapter : public output_adapter_protocol { public: explicit output_stream_adapter(std::basic_ostream& s) noexcept : stream(s) {} void write_character(CharType c) override { stream.put(c); } JSON_HEDLEY_NON_NULL(2) void write_characters(const CharType* s, std::size_t length) override { stream.write(s, static_cast(length)); } private: std::basic_ostream& stream; }; #endif // JSON_NO_IO /// output adapter for basic_string template> class output_string_adapter : public output_adapter_protocol { public: explicit output_string_adapter(StringType& s) noexcept : str(s) {} void write_character(CharType c) override { str.push_back(c); } JSON_HEDLEY_NON_NULL(2) void write_characters(const CharType* s, std::size_t length) override { str.append(s, length); } private: StringType& str; }; template> class output_adapter { public: template> output_adapter(std::vector& vec) : oa(std::make_shared>(vec)) {} #ifndef JSON_NO_IO output_adapter(std::basic_ostream& s) : oa(std::make_shared>(s)) {} #endif // JSON_NO_IO output_adapter(StringType& s) : oa(std::make_shared>(s)) {} operator output_adapter_t() { return oa; } private: output_adapter_t oa = nullptr; }; } // namespace detail } // namespace nlohmann namespace nlohmann { namespace detail { /////////////////// // binary writer // /////////////////// /*! @brief serialization to CBOR and MessagePack values */ template class binary_writer { using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; using number_float_t = typename BasicJsonType::number_float_t; public: /*! @brief create a binary writer @param[in] adapter output adapter to write to */ explicit binary_writer(output_adapter_t adapter) : oa(std::move(adapter)) { JSON_ASSERT(oa); } /*! @param[in] j JSON value to serialize @pre j.type() == value_t::object */ void write_bson(const BasicJsonType& j) { switch (j.type()) { case value_t::object: { write_bson_object(*j.m_value.object); break; } case value_t::null: case value_t::array: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); } } } /*! @param[in] j JSON value to serialize */ void write_cbor(const BasicJsonType& j) { switch (j.type()) { case value_t::null: { oa->write_character(to_char_type(0xF6)); break; } case value_t::boolean: { oa->write_character(j.m_value.boolean ? to_char_type(0xF5) : to_char_type(0xF4)); break; } case value_t::number_integer: { if (j.m_value.number_integer >= 0) { // CBOR does not differentiate between positive signed // integers and unsigned integers. Therefore, we used the // code from the value_t::number_unsigned case here. if (j.m_value.number_integer <= 0x17) { write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x18)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x19)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x1A)); write_number(static_cast(j.m_value.number_integer)); } else { oa->write_character(to_char_type(0x1B)); write_number(static_cast(j.m_value.number_integer)); } } else { // The conversions below encode the sign in the first // byte, and the value is converted to a positive number. const auto positive_number = -1 - j.m_value.number_integer; if (j.m_value.number_integer >= -24) { write_number(static_cast(0x20 + positive_number)); } else if (positive_number <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x38)); write_number(static_cast(positive_number)); } else if (positive_number <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x39)); write_number(static_cast(positive_number)); } else if (positive_number <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x3A)); write_number(static_cast(positive_number)); } else { oa->write_character(to_char_type(0x3B)); write_number(static_cast(positive_number)); } } break; } case value_t::number_unsigned: { if (j.m_value.number_unsigned <= 0x17) { write_number(static_cast(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x18)); write_number(static_cast(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x19)); write_number(static_cast(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x1A)); write_number(static_cast(j.m_value.number_unsigned)); } else { oa->write_character(to_char_type(0x1B)); write_number(static_cast(j.m_value.number_unsigned)); } break; } case value_t::number_float: { if (std::isnan(j.m_value.number_float)) { // NaN is 0xf97e00 in CBOR oa->write_character(to_char_type(0xF9)); oa->write_character(to_char_type(0x7E)); oa->write_character(to_char_type(0x00)); } else if (std::isinf(j.m_value.number_float)) { // Infinity is 0xf97c00, -Infinity is 0xf9fc00 oa->write_character(to_char_type(0xf9)); oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); oa->write_character(to_char_type(0x00)); } else { write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); } break; } case value_t::string: { // step 1: write control byte and the string length const auto N = j.m_value.string->size(); if (N <= 0x17) { write_number(static_cast(0x60 + N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x78)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x79)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x7A)); write_number(static_cast(N)); } // LCOV_EXCL_START else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x7B)); write_number(static_cast(N)); } // LCOV_EXCL_STOP // step 2: write the string oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); break; } case value_t::array: { // step 1: write control byte and the array size const auto N = j.m_value.array->size(); if (N <= 0x17) { write_number(static_cast(0x80 + N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x98)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x99)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x9A)); write_number(static_cast(N)); } // LCOV_EXCL_START else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x9B)); write_number(static_cast(N)); } // LCOV_EXCL_STOP // step 2: write each element for (const auto& el : *j.m_value.array) { write_cbor(el); } break; } case value_t::binary: { if (j.m_value.binary->has_subtype()) { if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) { write_number(static_cast(0xd8)); write_number(static_cast(j.m_value.binary->subtype())); } else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) { write_number(static_cast(0xd9)); write_number(static_cast(j.m_value.binary->subtype())); } else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) { write_number(static_cast(0xda)); write_number(static_cast(j.m_value.binary->subtype())); } else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) { write_number(static_cast(0xdb)); write_number(static_cast(j.m_value.binary->subtype())); } } // step 1: write control byte and the binary array size const auto N = j.m_value.binary->size(); if (N <= 0x17) { write_number(static_cast(0x40 + N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x58)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x59)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x5A)); write_number(static_cast(N)); } // LCOV_EXCL_START else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0x5B)); write_number(static_cast(N)); } // LCOV_EXCL_STOP // step 2: write each element oa->write_characters( reinterpret_cast(j.m_value.binary->data()), N); break; } case value_t::object: { // step 1: write control byte and the object size const auto N = j.m_value.object->size(); if (N <= 0x17) { write_number(static_cast(0xA0 + N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0xB8)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0xB9)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0xBA)); write_number(static_cast(N)); } // LCOV_EXCL_START else if (N <= (std::numeric_limits::max)()) { oa->write_character(to_char_type(0xBB)); write_number(static_cast(N)); } // LCOV_EXCL_STOP // step 2: write each element for (const auto& el : *j.m_value.object) { write_cbor(el.first); write_cbor(el.second); } break; } case value_t::discarded: default: break; } } /*! @param[in] j JSON value to serialize */ void write_msgpack(const BasicJsonType& j) { switch (j.type()) { case value_t::null: // nil { oa->write_character(to_char_type(0xC0)); break; } case value_t::boolean: // true and false { oa->write_character(j.m_value.boolean ? to_char_type(0xC3) : to_char_type(0xC2)); break; } case value_t::number_integer: { if (j.m_value.number_integer >= 0) { // MessagePack does not differentiate between positive // signed integers and unsigned integers. Therefore, we used // the code from the value_t::number_unsigned case here. if (j.m_value.number_unsigned < 128) { // positive fixnum write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 8 oa->write_character(to_char_type(0xCC)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 16 oa->write_character(to_char_type(0xCD)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 32 oa->write_character(to_char_type(0xCE)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 64 oa->write_character(to_char_type(0xCF)); write_number(static_cast(j.m_value.number_integer)); } } else { if (j.m_value.number_integer >= -32) { // negative fixnum write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits::min)() && j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 8 oa->write_character(to_char_type(0xD0)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits::min)() && j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 16 oa->write_character(to_char_type(0xD1)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits::min)() && j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 32 oa->write_character(to_char_type(0xD2)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits::min)() && j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 64 oa->write_character(to_char_type(0xD3)); write_number(static_cast(j.m_value.number_integer)); } } break; } case value_t::number_unsigned: { if (j.m_value.number_unsigned < 128) { // positive fixnum write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 8 oa->write_character(to_char_type(0xCC)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 16 oa->write_character(to_char_type(0xCD)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 32 oa->write_character(to_char_type(0xCE)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 64 oa->write_character(to_char_type(0xCF)); write_number(static_cast(j.m_value.number_integer)); } break; } case value_t::number_float: { write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } case value_t::string: { // step 1: write control byte and the string length const auto N = j.m_value.string->size(); if (N <= 31) { // fixstr write_number(static_cast(0xA0 | N)); } else if (N <= (std::numeric_limits::max)()) { // str 8 oa->write_character(to_char_type(0xD9)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { // str 16 oa->write_character(to_char_type(0xDA)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { // str 32 oa->write_character(to_char_type(0xDB)); write_number(static_cast(N)); } // step 2: write the string oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); break; } case value_t::array: { // step 1: write control byte and the array size const auto N = j.m_value.array->size(); if (N <= 15) { // fixarray write_number(static_cast(0x90 | N)); } else if (N <= (std::numeric_limits::max)()) { // array 16 oa->write_character(to_char_type(0xDC)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { // array 32 oa->write_character(to_char_type(0xDD)); write_number(static_cast(N)); } // step 2: write each element for (const auto& el : *j.m_value.array) { write_msgpack(el); } break; } case value_t::binary: { // step 0: determine if the binary type has a set subtype to // determine whether or not to use the ext or fixext types const bool use_ext = j.m_value.binary->has_subtype(); // step 1: write control byte and the byte string length const auto N = j.m_value.binary->size(); if (N <= (std::numeric_limits::max)()) { std::uint8_t output_type{}; bool fixed = true; if (use_ext) { switch (N) { case 1: output_type = 0xD4; // fixext 1 break; case 2: output_type = 0xD5; // fixext 2 break; case 4: output_type = 0xD6; // fixext 4 break; case 8: output_type = 0xD7; // fixext 8 break; case 16: output_type = 0xD8; // fixext 16 break; default: output_type = 0xC7; // ext 8 fixed = false; break; } } else { output_type = 0xC4; // bin 8 fixed = false; } oa->write_character(to_char_type(output_type)); if (!fixed) { write_number(static_cast(N)); } } else if (N <= (std::numeric_limits::max)()) { std::uint8_t output_type = use_ext ? 0xC8 // ext 16 : 0xC5; // bin 16 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { std::uint8_t output_type = use_ext ? 0xC9 // ext 32 : 0xC6; // bin 32 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); } // step 1.5: if this is an ext type, write the subtype if (use_ext) { write_number(static_cast(j.m_value.binary->subtype())); } // step 2: write the byte string oa->write_characters( reinterpret_cast(j.m_value.binary->data()), N); break; } case value_t::object: { // step 1: write control byte and the object size const auto N = j.m_value.object->size(); if (N <= 15) { // fixmap write_number(static_cast(0x80 | (N & 0xF))); } else if (N <= (std::numeric_limits::max)()) { // map 16 oa->write_character(to_char_type(0xDE)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { // map 32 oa->write_character(to_char_type(0xDF)); write_number(static_cast(N)); } // step 2: write each element for (const auto& el : *j.m_value.object) { write_msgpack(el.first); write_msgpack(el.second); } break; } case value_t::discarded: default: break; } } /*! @param[in] j JSON value to serialize @param[in] use_count whether to use '#' prefixes (optimized format) @param[in] use_type whether to use '$' prefixes (optimized format) @param[in] add_prefix whether prefixes need to be used for this value */ void write_ubjson(const BasicJsonType& j, const bool use_count, const bool use_type, const bool add_prefix = true) { switch (j.type()) { case value_t::null: { if (add_prefix) { oa->write_character(to_char_type('Z')); } break; } case value_t::boolean: { if (add_prefix) { oa->write_character(j.m_value.boolean ? to_char_type('T') : to_char_type('F')); } break; } case value_t::number_integer: { write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); break; } case value_t::number_unsigned: { write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); break; } case value_t::number_float: { write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); break; } case value_t::string: { if (add_prefix) { oa->write_character(to_char_type('S')); } write_number_with_ubjson_prefix(j.m_value.string->size(), true); oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); break; } case value_t::array: { if (add_prefix) { oa->write_character(to_char_type('[')); } bool prefix_required = true; if (use_type && !j.m_value.array->empty()) { JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), [this, first_prefix](const BasicJsonType & v) { return ubjson_prefix(v) == first_prefix; }); if (same_prefix) { prefix_required = false; oa->write_character(to_char_type('$')); oa->write_character(first_prefix); } } if (use_count) { oa->write_character(to_char_type('#')); write_number_with_ubjson_prefix(j.m_value.array->size(), true); } for (const auto& el : *j.m_value.array) { write_ubjson(el, use_count, use_type, prefix_required); } if (!use_count) { oa->write_character(to_char_type(']')); } break; } case value_t::binary: { if (add_prefix) { oa->write_character(to_char_type('[')); } if (use_type && !j.m_value.binary->empty()) { JSON_ASSERT(use_count); oa->write_character(to_char_type('$')); oa->write_character('U'); } if (use_count) { oa->write_character(to_char_type('#')); write_number_with_ubjson_prefix(j.m_value.binary->size(), true); } if (use_type) { oa->write_characters( reinterpret_cast(j.m_value.binary->data()), j.m_value.binary->size()); } else { for (size_t i = 0; i < j.m_value.binary->size(); ++i) { oa->write_character(to_char_type('U')); oa->write_character(j.m_value.binary->data()[i]); } } if (!use_count) { oa->write_character(to_char_type(']')); } break; } case value_t::object: { if (add_prefix) { oa->write_character(to_char_type('{')); } bool prefix_required = true; if (use_type && !j.m_value.object->empty()) { JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin(), j.end(), [this, first_prefix](const BasicJsonType & v) { return ubjson_prefix(v) == first_prefix; }); if (same_prefix) { prefix_required = false; oa->write_character(to_char_type('$')); oa->write_character(first_prefix); } } if (use_count) { oa->write_character(to_char_type('#')); write_number_with_ubjson_prefix(j.m_value.object->size(), true); } for (const auto& el : *j.m_value.object) { write_number_with_ubjson_prefix(el.first.size(), true); oa->write_characters( reinterpret_cast(el.first.c_str()), el.first.size()); write_ubjson(el.second, use_count, use_type, prefix_required); } if (!use_count) { oa->write_character(to_char_type('}')); } break; } case value_t::discarded: default: break; } } private: ////////// // BSON // ////////// /*! @return The size of a BSON document entry header, including the id marker and the entry name size (and its null-terminator). */ static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) { const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); static_cast(j); } return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; } /*! @brief Writes the given @a element_type and @a name to the output adapter */ void write_bson_entry_header(const string_t& name, const std::uint8_t element_type) { oa->write_character(to_char_type(element_type)); // boolean oa->write_characters( reinterpret_cast(name.c_str()), name.size() + 1u); } /*! @brief Writes a BSON element with key @a name and boolean value @a value */ void write_bson_boolean(const string_t& name, const bool value) { write_bson_entry_header(name, 0x08); oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); } /*! @brief Writes a BSON element with key @a name and double value @a value */ void write_bson_double(const string_t& name, const double value) { write_bson_entry_header(name, 0x01); write_number(value); } /*! @return The size of the BSON-encoded string in @a value */ static std::size_t calc_bson_string_size(const string_t& value) { return sizeof(std::int32_t) + value.size() + 1ul; } /*! @brief Writes a BSON element with key @a name and string value @a value */ void write_bson_string(const string_t& name, const string_t& value) { write_bson_entry_header(name, 0x02); write_number(static_cast(value.size() + 1ul)); oa->write_characters( reinterpret_cast(value.c_str()), value.size() + 1); } /*! @brief Writes a BSON element with key @a name and null value */ void write_bson_null(const string_t& name) { write_bson_entry_header(name, 0x0A); } /*! @return The size of the BSON-encoded integer @a value */ static std::size_t calc_bson_integer_size(const std::int64_t value) { return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() ? sizeof(std::int32_t) : sizeof(std::int64_t); } /*! @brief Writes a BSON element with key @a name and integer @a value */ void write_bson_integer(const string_t& name, const std::int64_t value) { if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) { write_bson_entry_header(name, 0x10); // int32 write_number(static_cast(value)); } else { write_bson_entry_header(name, 0x12); // int64 write_number(static_cast(value)); } } /*! @return The size of the BSON-encoded unsigned integer in @a j */ static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept { return (value <= static_cast((std::numeric_limits::max)())) ? sizeof(std::int32_t) : sizeof(std::int64_t); } /*! @brief Writes a BSON element with key @a name and unsigned @a value */ void write_bson_unsigned(const string_t& name, const BasicJsonType& j) { if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x10 /* int32 */); write_number(static_cast(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x12 /* int64 */); write_number(static_cast(j.m_value.number_unsigned)); } else { JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); } } /*! @brief Writes a BSON element with key @a name and object @a value */ void write_bson_object_entry(const string_t& name, const typename BasicJsonType::object_t& value) { write_bson_entry_header(name, 0x03); // object write_bson_object(value); } /*! @return The size of the BSON-encoded array @a value */ static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) { std::size_t array_index = 0ul; const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) { return result + calc_bson_element_size(std::to_string(array_index++), el); }); return sizeof(std::int32_t) + embedded_document_size + 1ul; } /*! @return The size of the BSON-encoded binary array @a value */ static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) { return sizeof(std::int32_t) + value.size() + 1ul; } /*! @brief Writes a BSON element with key @a name and array @a value */ void write_bson_array(const string_t& name, const typename BasicJsonType::array_t& value) { write_bson_entry_header(name, 0x04); // array write_number(static_cast(calc_bson_array_size(value))); std::size_t array_index = 0ul; for (const auto& el : value) { write_bson_element(std::to_string(array_index++), el); } oa->write_character(to_char_type(0x00)); } /*! @brief Writes a BSON element with key @a name and binary value @a value */ void write_bson_binary(const string_t& name, const binary_t& value) { write_bson_entry_header(name, 0x05); write_number(static_cast(value.size())); write_number(value.has_subtype() ? static_cast(value.subtype()) : std::uint8_t(0x00)); oa->write_characters(reinterpret_cast(value.data()), value.size()); } /*! @brief Calculates the size necessary to serialize the JSON value @a j with its @a name @return The calculated size for the BSON document entry for @a j with the given @a name. */ static std::size_t calc_bson_element_size(const string_t& name, const BasicJsonType& j) { const auto header_size = calc_bson_entry_header_size(name, j); switch (j.type()) { case value_t::object: return header_size + calc_bson_object_size(*j.m_value.object); case value_t::array: return header_size + calc_bson_array_size(*j.m_value.array); case value_t::binary: return header_size + calc_bson_binary_size(*j.m_value.binary); case value_t::boolean: return header_size + 1ul; case value_t::number_float: return header_size + 8ul; case value_t::number_integer: return header_size + calc_bson_integer_size(j.m_value.number_integer); case value_t::number_unsigned: return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); case value_t::string: return header_size + calc_bson_string_size(*j.m_value.string); case value_t::null: return header_size + 0ul; // LCOV_EXCL_START case value_t::discarded: default: JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) return 0ul; // LCOV_EXCL_STOP } } /*! @brief Serializes the JSON value @a j to BSON and associates it with the key @a name. @param name The name to associate with the JSON entity @a j within the current BSON document */ void write_bson_element(const string_t& name, const BasicJsonType& j) { switch (j.type()) { case value_t::object: return write_bson_object_entry(name, *j.m_value.object); case value_t::array: return write_bson_array(name, *j.m_value.array); case value_t::binary: return write_bson_binary(name, *j.m_value.binary); case value_t::boolean: return write_bson_boolean(name, j.m_value.boolean); case value_t::number_float: return write_bson_double(name, j.m_value.number_float); case value_t::number_integer: return write_bson_integer(name, j.m_value.number_integer); case value_t::number_unsigned: return write_bson_unsigned(name, j); case value_t::string: return write_bson_string(name, *j.m_value.string); case value_t::null: return write_bson_null(name); // LCOV_EXCL_START case value_t::discarded: default: JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) return; // LCOV_EXCL_STOP } } /*! @brief Calculates the size of the BSON serialization of the given JSON-object @a j. @param[in] value JSON value to serialize @pre value.type() == value_t::object */ static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) { std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0), [](size_t result, const typename BasicJsonType::object_t::value_type & el) { return result += calc_bson_element_size(el.first, el.second); }); return sizeof(std::int32_t) + document_size + 1ul; } /*! @param[in] value JSON value to serialize @pre value.type() == value_t::object */ void write_bson_object(const typename BasicJsonType::object_t& value) { write_number(static_cast(calc_bson_object_size(value))); for (const auto& el : value) { write_bson_element(el.first, el.second); } oa->write_character(to_char_type(0x00)); } ////////// // CBOR // ////////// static constexpr CharType get_cbor_float_prefix(float /*unused*/) { return to_char_type(0xFA); // Single-Precision Float } static constexpr CharType get_cbor_float_prefix(double /*unused*/) { return to_char_type(0xFB); // Double-Precision Float } ///////////// // MsgPack // ///////////// static constexpr CharType get_msgpack_float_prefix(float /*unused*/) { return to_char_type(0xCA); // float 32 } static constexpr CharType get_msgpack_float_prefix(double /*unused*/) { return to_char_type(0xCB); // float 64 } //////////// // UBJSON // //////////// // UBJSON: write number (floating point) template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { if (add_prefix) { oa->write_character(get_ubjson_float_prefix(n)); } write_number(n); } // UBJSON: write number (unsigned integer) template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(to_char_type('i')); // int8 } write_number(static_cast(n)); } else if (n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(to_char_type('U')); // uint8 } write_number(static_cast(n)); } else if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(to_char_type('I')); // int16 } write_number(static_cast(n)); } else if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(to_char_type('l')); // int32 } write_number(static_cast(n)); } else if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(to_char_type('L')); // int64 } write_number(static_cast(n)); } else { if (add_prefix) { oa->write_character(to_char_type('H')); // high-precision number } const auto number = BasicJsonType(n).dump(); write_number_with_ubjson_prefix(number.size(), true); for (std::size_t i = 0; i < number.size(); ++i) { oa->write_character(to_char_type(static_cast(number[i]))); } } } // UBJSON: write number (signed integer) template < typename NumberType, typename std::enable_if < std::is_signed::value&& !std::is_floating_point::value, int >::type = 0 > void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(to_char_type('i')); // int8 } write_number(static_cast(n)); } else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(to_char_type('U')); // uint8 } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(to_char_type('I')); // int16 } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(to_char_type('l')); // int32 } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(to_char_type('L')); // int64 } write_number(static_cast(n)); } // LCOV_EXCL_START else { if (add_prefix) { oa->write_character(to_char_type('H')); // high-precision number } const auto number = BasicJsonType(n).dump(); write_number_with_ubjson_prefix(number.size(), true); for (std::size_t i = 0; i < number.size(); ++i) { oa->write_character(to_char_type(static_cast(number[i]))); } } // LCOV_EXCL_STOP } /*! @brief determine the type prefix of container values */ CharType ubjson_prefix(const BasicJsonType& j) const noexcept { switch (j.type()) { case value_t::null: return 'Z'; case value_t::boolean: return j.m_value.boolean ? 'T' : 'F'; case value_t::number_integer: { if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'i'; } if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'U'; } if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'I'; } if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'l'; } if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'L'; } // anything else is treated as high-precision number return 'H'; // LCOV_EXCL_LINE } case value_t::number_unsigned: { if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'i'; } if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'U'; } if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'I'; } if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'l'; } if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'L'; } // anything else is treated as high-precision number return 'H'; // LCOV_EXCL_LINE } case value_t::number_float: return get_ubjson_float_prefix(j.m_value.number_float); case value_t::string: return 'S'; case value_t::array: // fallthrough case value_t::binary: return '['; case value_t::object: return '{'; case value_t::discarded: default: // discarded values return 'N'; } } static constexpr CharType get_ubjson_float_prefix(float /*unused*/) { return 'd'; // float 32 } static constexpr CharType get_ubjson_float_prefix(double /*unused*/) { return 'D'; // float 64 } /////////////////////// // Utility functions // /////////////////////// /* @brief write a number to output input @param[in] n number of type @a NumberType @tparam NumberType the type of the number @tparam OutputIsLittleEndian Set to true if output data is required to be little endian @note This function needs to respect the system's endianess, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ template void write_number(const NumberType n) { // step 1: write number to array of length NumberType std::array vec{}; std::memcpy(vec.data(), &n, sizeof(NumberType)); // step 2: write array to output (with possible reordering) if (is_little_endian != OutputIsLittleEndian) { // reverse byte order prior to conversion if necessary std::reverse(vec.begin(), vec.end()); } oa->write_characters(vec.data(), sizeof(NumberType)); } void write_compact_float(const number_float_t n, detail::input_format_t format) { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && static_cast(n) <= static_cast((std::numeric_limits::max)()) && static_cast(static_cast(n)) == static_cast(n)) { oa->write_character(format == detail::input_format_t::cbor ? get_cbor_float_prefix(static_cast(n)) : get_msgpack_float_prefix(static_cast(n))); write_number(static_cast(n)); } else { oa->write_character(format == detail::input_format_t::cbor ? get_cbor_float_prefix(n) : get_msgpack_float_prefix(n)); write_number(n); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif } public: // The following to_char_type functions are implement the conversion // between uint8_t and CharType. In case CharType is not unsigned, // such a conversion is required to allow values greater than 128. // See for a discussion. template < typename C = CharType, enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > static constexpr CharType to_char_type(std::uint8_t x) noexcept { return *reinterpret_cast(&x); } template < typename C = CharType, enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > static CharType to_char_type(std::uint8_t x) noexcept { static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); static_assert(std::is_trivial::value, "CharType must be trivial"); CharType result; std::memcpy(&result, &x, sizeof(x)); return result; } template::value>* = nullptr> static constexpr CharType to_char_type(std::uint8_t x) noexcept { return x; } template < typename InputCharType, typename C = CharType, enable_if_t < std::is_signed::value && std::is_signed::value && std::is_same::type>::value > * = nullptr > static constexpr CharType to_char_type(InputCharType x) noexcept { return x; } private: /// whether we can assume little endianess const bool is_little_endian = little_endianess(); /// the output output_adapter_t oa = nullptr; }; } // namespace detail } // namespace nlohmann // #include // #include #include // reverse, remove, fill, find, none_of #include // array #include // localeconv, lconv #include // labs, isfinite, isnan, signbit #include // size_t, ptrdiff_t #include // uint8_t #include // snprintf #include // numeric_limits #include // string, char_traits #include // is_same #include // move // #include #include // array #include // signbit, isfinite #include // intN_t, uintN_t #include // memcpy, memmove #include // numeric_limits #include // conditional // #include namespace nlohmann { namespace detail { /*! @brief implements the Grisu2 algorithm for binary to decimal floating-point conversion. This implementation is a slightly modified version of the reference implementation which may be obtained from http://florian.loitsch.com/publications (bench.tar.gz). The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. For a detailed description of the algorithm see: [1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, PLDI 2010 [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation, PLDI 1996 */ namespace dtoa_impl { template Target reinterpret_bits(const Source source) { static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); Target target; std::memcpy(&target, &source, sizeof(Source)); return target; } struct diyfp // f * 2^e { static constexpr int kPrecision = 64; // = q std::uint64_t f = 0; int e = 0; constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} /*! @brief returns x - y @pre x.e == y.e and x.f >= y.f */ static diyfp sub(const diyfp& x, const diyfp& y) noexcept { JSON_ASSERT(x.e == y.e); JSON_ASSERT(x.f >= y.f); return {x.f - y.f, x.e}; } /*! @brief returns x * y @note The result is rounded. (Only the upper q bits are returned.) */ static diyfp mul(const diyfp& x, const diyfp& y) noexcept { static_assert(kPrecision == 64, "internal error"); // Computes: // f = round((x.f * y.f) / 2^q) // e = x.e + y.e + q // Emulate the 64-bit * 64-bit multiplication: // // p = u * v // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) // // (Since Q might be larger than 2^32 - 1) // // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) // // (Q_hi + H does not overflow a 64-bit int) // // = p_lo + 2^64 p_hi const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; const std::uint64_t u_hi = x.f >> 32u; const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; const std::uint64_t v_hi = y.f >> 32u; const std::uint64_t p0 = u_lo * v_lo; const std::uint64_t p1 = u_lo * v_hi; const std::uint64_t p2 = u_hi * v_lo; const std::uint64_t p3 = u_hi * v_hi; const std::uint64_t p0_hi = p0 >> 32u; const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; const std::uint64_t p1_hi = p1 >> 32u; const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; const std::uint64_t p2_hi = p2 >> 32u; std::uint64_t Q = p0_hi + p1_lo + p2_lo; // The full product might now be computed as // // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) // p_lo = p0_lo + (Q << 32) // // But in this particular case here, the full p_lo is not required. // Effectively we only need to add the highest bit in p_lo to p_hi (and // Q_hi + 1 does not overflow). Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); return {h, x.e + y.e + 64}; } /*! @brief normalize x such that the significand is >= 2^(q-1) @pre x.f != 0 */ static diyfp normalize(diyfp x) noexcept { JSON_ASSERT(x.f != 0); while ((x.f >> 63u) == 0) { x.f <<= 1u; x.e--; } return x; } /*! @brief normalize x such that the result has the exponent E @pre e >= x.e and the upper e - x.e bits of x.f must be zero. */ static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept { const int delta = x.e - target_exponent; JSON_ASSERT(delta >= 0); JSON_ASSERT(((x.f << delta) >> delta) == x.f); return {x.f << delta, target_exponent}; } }; struct boundaries { diyfp w; diyfp minus; diyfp plus; }; /*! Compute the (normalized) diyfp representing the input number 'value' and its boundaries. @pre value must be finite and positive */ template boundaries compute_boundaries(FloatType value) { JSON_ASSERT(std::isfinite(value)); JSON_ASSERT(value > 0); // Convert the IEEE representation into a diyfp. // // If v is denormal: // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) // If v is normalized: // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) static_assert(std::numeric_limits::is_iec559, "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); constexpr int kMinExp = 1 - kBias; constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) using bits_type = typename std::conditional::type; const auto bits = static_cast(reinterpret_bits(value)); const std::uint64_t E = bits >> (kPrecision - 1); const std::uint64_t F = bits & (kHiddenBit - 1); const bool is_denormal = E == 0; const diyfp v = is_denormal ? diyfp(F, kMinExp) : diyfp(F + kHiddenBit, static_cast(E) - kBias); // Compute the boundaries m- and m+ of the floating-point value // v = f * 2^e. // // Determine v- and v+, the floating-point predecessor and successor if v, // respectively. // // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) // // v+ = v + 2^e // // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ // between m- and m+ round to v, regardless of how the input rounding // algorithm breaks ties. // // ---+-------------+-------------+-------------+-------------+--- (A) // v- m- v m+ v+ // // -----------------+------+------+-------------+-------------+--- (B) // v- m- v m+ v+ const bool lower_boundary_is_closer = F == 0 && E > 1; const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); const diyfp m_minus = lower_boundary_is_closer ? diyfp(4 * v.f - 1, v.e - 2) // (B) : diyfp(2 * v.f - 1, v.e - 1); // (A) // Determine the normalized w+ = m+. const diyfp w_plus = diyfp::normalize(m_plus); // Determine w- = m- such that e_(w-) = e_(w+). const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); return {diyfp::normalize(v), w_minus, w_plus}; } // Given normalized diyfp w, Grisu needs to find a (normalized) cached // power-of-ten c, such that the exponent of the product c * w = f * 2^e lies // within a certain range [alpha, gamma] (Definition 3.2 from [1]) // // alpha <= e = e_c + e_w + q <= gamma // // or // // f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q // <= f_c * f_w * 2^gamma // // Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies // // 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma // // or // // 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) // // The choice of (alpha,gamma) determines the size of the table and the form of // the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well // in practice: // // The idea is to cut the number c * w = f * 2^e into two parts, which can be // processed independently: An integral part p1, and a fractional part p2: // // f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e // = (f div 2^-e) + (f mod 2^-e) * 2^e // = p1 + p2 * 2^e // // The conversion of p1 into decimal form requires a series of divisions and // modulos by (a power of) 10. These operations are faster for 32-bit than for // 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be // achieved by choosing // // -e >= 32 or e <= -32 := gamma // // In order to convert the fractional part // // p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... // // into decimal form, the fraction is repeatedly multiplied by 10 and the digits // d[-i] are extracted in order: // // (10 * p2) div 2^-e = d[-1] // (10 * p2) mod 2^-e = d[-2] / 10^1 + ... // // The multiplication by 10 must not overflow. It is sufficient to choose // // 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. // // Since p2 = f mod 2^-e < 2^-e, // // -e <= 60 or e >= -60 := alpha constexpr int kAlpha = -60; constexpr int kGamma = -32; struct cached_power // c = f * 2^e ~= 10^k { std::uint64_t f; int e; int k; }; /*! For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c satisfies (Definition 3.2 from [1]) alpha <= e_c + e + q <= gamma. */ inline cached_power get_cached_power_for_binary_exponent(int e) { // Now // // alpha <= e_c + e + q <= gamma (1) // ==> f_c * 2^alpha <= c * 2^e * 2^q // // and since the c's are normalized, 2^(q-1) <= f_c, // // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) // ==> 2^(alpha - e - 1) <= c // // If c were an exact power of ten, i.e. c = 10^k, one may determine k as // // k = ceil( log_10( 2^(alpha - e - 1) ) ) // = ceil( (alpha - e - 1) * log_10(2) ) // // From the paper: // "In theory the result of the procedure could be wrong since c is rounded, // and the computation itself is approximated [...]. In practice, however, // this simple function is sufficient." // // For IEEE double precision floating-point numbers converted into // normalized diyfp's w = f * 2^e, with q = 64, // // e >= -1022 (min IEEE exponent) // -52 (p - 1) // -52 (p - 1, possibly normalize denormal IEEE numbers) // -11 (normalize the diyfp) // = -1137 // // and // // e <= +1023 (max IEEE exponent) // -52 (p - 1) // -11 (normalize the diyfp) // = 960 // // This binary exponent range [-1137,960] results in a decimal exponent // range [-307,324]. One does not need to store a cached power for each // k in this range. For each such k it suffices to find a cached power // such that the exponent of the product lies in [alpha,gamma]. // This implies that the difference of the decimal exponents of adjacent // table entries must be less than or equal to // // floor( (gamma - alpha) * log_10(2) ) = 8. // // (A smaller distance gamma-alpha would require a larger table.) // NB: // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. constexpr int kCachedPowersMinDecExp = -300; constexpr int kCachedPowersDecStep = 8; static constexpr std::array kCachedPowers = { { { 0xAB70FE17C79AC6CA, -1060, -300 }, { 0xFF77B1FCBEBCDC4F, -1034, -292 }, { 0xBE5691EF416BD60C, -1007, -284 }, { 0x8DD01FAD907FFC3C, -980, -276 }, { 0xD3515C2831559A83, -954, -268 }, { 0x9D71AC8FADA6C9B5, -927, -260 }, { 0xEA9C227723EE8BCB, -901, -252 }, { 0xAECC49914078536D, -874, -244 }, { 0x823C12795DB6CE57, -847, -236 }, { 0xC21094364DFB5637, -821, -228 }, { 0x9096EA6F3848984F, -794, -220 }, { 0xD77485CB25823AC7, -768, -212 }, { 0xA086CFCD97BF97F4, -741, -204 }, { 0xEF340A98172AACE5, -715, -196 }, { 0xB23867FB2A35B28E, -688, -188 }, { 0x84C8D4DFD2C63F3B, -661, -180 }, { 0xC5DD44271AD3CDBA, -635, -172 }, { 0x936B9FCEBB25C996, -608, -164 }, { 0xDBAC6C247D62A584, -582, -156 }, { 0xA3AB66580D5FDAF6, -555, -148 }, { 0xF3E2F893DEC3F126, -529, -140 }, { 0xB5B5ADA8AAFF80B8, -502, -132 }, { 0x87625F056C7C4A8B, -475, -124 }, { 0xC9BCFF6034C13053, -449, -116 }, { 0x964E858C91BA2655, -422, -108 }, { 0xDFF9772470297EBD, -396, -100 }, { 0xA6DFBD9FB8E5B88F, -369, -92 }, { 0xF8A95FCF88747D94, -343, -84 }, { 0xB94470938FA89BCF, -316, -76 }, { 0x8A08F0F8BF0F156B, -289, -68 }, { 0xCDB02555653131B6, -263, -60 }, { 0x993FE2C6D07B7FAC, -236, -52 }, { 0xE45C10C42A2B3B06, -210, -44 }, { 0xAA242499697392D3, -183, -36 }, { 0xFD87B5F28300CA0E, -157, -28 }, { 0xBCE5086492111AEB, -130, -20 }, { 0x8CBCCC096F5088CC, -103, -12 }, { 0xD1B71758E219652C, -77, -4 }, { 0x9C40000000000000, -50, 4 }, { 0xE8D4A51000000000, -24, 12 }, { 0xAD78EBC5AC620000, 3, 20 }, { 0x813F3978F8940984, 30, 28 }, { 0xC097CE7BC90715B3, 56, 36 }, { 0x8F7E32CE7BEA5C70, 83, 44 }, { 0xD5D238A4ABE98068, 109, 52 }, { 0x9F4F2726179A2245, 136, 60 }, { 0xED63A231D4C4FB27, 162, 68 }, { 0xB0DE65388CC8ADA8, 189, 76 }, { 0x83C7088E1AAB65DB, 216, 84 }, { 0xC45D1DF942711D9A, 242, 92 }, { 0x924D692CA61BE758, 269, 100 }, { 0xDA01EE641A708DEA, 295, 108 }, { 0xA26DA3999AEF774A, 322, 116 }, { 0xF209787BB47D6B85, 348, 124 }, { 0xB454E4A179DD1877, 375, 132 }, { 0x865B86925B9BC5C2, 402, 140 }, { 0xC83553C5C8965D3D, 428, 148 }, { 0x952AB45CFA97A0B3, 455, 156 }, { 0xDE469FBD99A05FE3, 481, 164 }, { 0xA59BC234DB398C25, 508, 172 }, { 0xF6C69A72A3989F5C, 534, 180 }, { 0xB7DCBF5354E9BECE, 561, 188 }, { 0x88FCF317F22241E2, 588, 196 }, { 0xCC20CE9BD35C78A5, 614, 204 }, { 0x98165AF37B2153DF, 641, 212 }, { 0xE2A0B5DC971F303A, 667, 220 }, { 0xA8D9D1535CE3B396, 694, 228 }, { 0xFB9B7CD9A4A7443C, 720, 236 }, { 0xBB764C4CA7A44410, 747, 244 }, { 0x8BAB8EEFB6409C1A, 774, 252 }, { 0xD01FEF10A657842C, 800, 260 }, { 0x9B10A4E5E9913129, 827, 268 }, { 0xE7109BFBA19C0C9D, 853, 276 }, { 0xAC2820D9623BF429, 880, 284 }, { 0x80444B5E7AA7CF85, 907, 292 }, { 0xBF21E44003ACDD2D, 933, 300 }, { 0x8E679C2F5E44FF8F, 960, 308 }, { 0xD433179D9C8CB841, 986, 316 }, { 0x9E19DB92B4E31BA9, 1013, 324 }, } }; // This computation gives exactly the same results for k as // k = ceil((kAlpha - e - 1) * 0.30102999566398114) // for |e| <= 1500, but doesn't require floating-point operations. // NB: log_10(2) ~= 78913 / 2^18 JSON_ASSERT(e >= -1500); JSON_ASSERT(e <= 1500); const int f = kAlpha - e - 1; const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; JSON_ASSERT(index >= 0); JSON_ASSERT(static_cast(index) < kCachedPowers.size()); const cached_power cached = kCachedPowers[static_cast(index)]; JSON_ASSERT(kAlpha <= cached.e + e + 64); JSON_ASSERT(kGamma >= cached.e + e + 64); return cached; } /*! For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. For n == 0, returns 1 and sets pow10 := 1. */ inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) { // LCOV_EXCL_START if (n >= 1000000000) { pow10 = 1000000000; return 10; } // LCOV_EXCL_STOP if (n >= 100000000) { pow10 = 100000000; return 9; } if (n >= 10000000) { pow10 = 10000000; return 8; } if (n >= 1000000) { pow10 = 1000000; return 7; } if (n >= 100000) { pow10 = 100000; return 6; } if (n >= 10000) { pow10 = 10000; return 5; } if (n >= 1000) { pow10 = 1000; return 4; } if (n >= 100) { pow10 = 100; return 3; } if (n >= 10) { pow10 = 10; return 2; } pow10 = 1; return 1; } inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, std::uint64_t rest, std::uint64_t ten_k) { JSON_ASSERT(len >= 1); JSON_ASSERT(dist <= delta); JSON_ASSERT(rest <= delta); JSON_ASSERT(ten_k > 0); // <--------------------------- delta ----> // <---- dist ---------> // --------------[------------------+-------------------]-------------- // M- w M+ // // ten_k // <------> // <---- rest ----> // --------------[------------------+----+--------------]-------------- // w V // = buf * 10^k // // ten_k represents a unit-in-the-last-place in the decimal representation // stored in buf. // Decrement buf by ten_k while this takes buf closer to w. // The tests are written in this order to avoid overflow in unsigned // integer arithmetic. while (rest < dist && delta - rest >= ten_k && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) { JSON_ASSERT(buf[len - 1] != '0'); buf[len - 1]--; rest += ten_k; } } /*! Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. M- and M+ must be normalized and share the same exponent -60 <= e <= -32. */ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, diyfp M_minus, diyfp w, diyfp M_plus) { static_assert(kAlpha >= -60, "internal error"); static_assert(kGamma <= -32, "internal error"); // Generates the digits (and the exponent) of a decimal floating-point // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. // // <--------------------------- delta ----> // <---- dist ---------> // --------------[------------------+-------------------]-------------- // M- w M+ // // Grisu2 generates the digits of M+ from left to right and stops as soon as // V is in [M-,M+]. JSON_ASSERT(M_plus.e >= kAlpha); JSON_ASSERT(M_plus.e <= kGamma); std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): // // M+ = f * 2^e // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e // = ((p1 ) * 2^-e + (p2 )) * 2^e // = p1 + p2 * 2^e const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e // 1) // // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] JSON_ASSERT(p1 > 0); std::uint32_t pow10{}; const int k = find_largest_pow10(p1, pow10); // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) // // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) // // M+ = p1 + p2 * 2^e // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e // = d[k-1] * 10^(k-1) + ( rest) * 2^e // // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) // // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] // // but stop as soon as // // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e int n = k; while (n > 0) { // Invariants: // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) // pow10 = 10^(n-1) <= p1 < 10^n // const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) // // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) // JSON_ASSERT(d <= 9); buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) // p1 = r; n--; // // M+ = buffer * 10^n + (p1 + p2 * 2^e) // pow10 = 10^n // // Now check if enough digits have been generated. // Compute // // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e // // Note: // Since rest and delta share the same exponent e, it suffices to // compare the significands. const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; if (rest <= delta) { // V = buffer * 10^n, with M- <= V <= M+. decimal_exponent += n; // We may now just stop. But instead look if the buffer could be // decremented to bring V closer to w. // // pow10 = 10^n is now 1 ulp in the decimal representation V. // The rounding procedure works with diyfp's with an implicit // exponent of e. // // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e // const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; grisu2_round(buffer, length, dist, delta, rest, ten_n); return; } pow10 /= 10; // // pow10 = 10^(n-1) <= p1 < 10^n // Invariants restored. } // 2) // // The digits of the integral part have been generated: // // M+ = d[k-1]...d[1]d[0] + p2 * 2^e // = buffer + p2 * 2^e // // Now generate the digits of the fractional part p2 * 2^e. // // Note: // No decimal point is generated: the exponent is adjusted instead. // // p2 actually represents the fraction // // p2 * 2^e // = p2 / 2^-e // = d[-1] / 10^1 + d[-2] / 10^2 + ... // // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) // // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) // // using // // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) // = ( d) * 2^-e + ( r) // // or // 10^m * p2 * 2^e = d + r * 2^e // // i.e. // // M+ = buffer + p2 * 2^e // = buffer + 10^-m * (d + r * 2^e) // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e // // and stop as soon as 10^-m * r * 2^e <= delta * 2^e JSON_ASSERT(p2 > delta); int m = 0; for (;;) { // Invariant: // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e // = buffer * 10^-m + 10^-m * (p2 ) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e // JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); p2 *= 10; const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e // // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e // JSON_ASSERT(d <= 9); buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e // p2 = r; m++; // // M+ = buffer * 10^-m + 10^-m * p2 * 2^e // Invariant restored. // Check if enough digits have been generated. // // 10^-m * p2 * 2^e <= delta * 2^e // p2 * 2^e <= 10^m * delta * 2^e // p2 <= 10^m * delta delta *= 10; dist *= 10; if (p2 <= delta) { break; } } // V = buffer * 10^-m, with M- <= V <= M+. decimal_exponent -= m; // 1 ulp in the decimal representation is now 10^-m. // Since delta and dist are now scaled by 10^m, we need to do the // same with ulp in order to keep the units in sync. // // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e // const std::uint64_t ten_m = one.f; grisu2_round(buffer, length, dist, delta, p2, ten_m); // By construction this algorithm generates the shortest possible decimal // number (Loitsch, Theorem 6.2) which rounds back to w. // For an input number of precision p, at least // // N = 1 + ceil(p * log_10(2)) // // decimal digits are sufficient to identify all binary floating-point // numbers (Matula, "In-and-Out conversions"). // This implies that the algorithm does not produce more than N decimal // digits. // // N = 17 for p = 53 (IEEE double precision) // N = 9 for p = 24 (IEEE single precision) } /*! v = buf * 10^decimal_exponent len is the length of the buffer (number of decimal digits) The buffer must be large enough, i.e. >= max_digits10. */ JSON_HEDLEY_NON_NULL(1) inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, diyfp v, diyfp m_plus) { JSON_ASSERT(m_plus.e == m_minus.e); JSON_ASSERT(m_plus.e == v.e); // --------(-----------------------+-----------------------)-------- (A) // m- v m+ // // --------------------(-----------+-----------------------)-------- (B) // m- v m+ // // First scale v (and m- and m+) such that the exponent is in the range // [alpha, gamma]. const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] const diyfp w = diyfp::mul(v, c_minus_k); const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); // ----(---+---)---------------(---+---)---------------(---+---)---- // w- w w+ // = c*m- = c*v = c*m+ // // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and // w+ are now off by a small amount. // In fact: // // w - v * 10^k < 1 ulp // // To account for this inaccuracy, add resp. subtract 1 ulp. // // --------+---[---------------(---+---)---------------]---+-------- // w- M- w M+ w+ // // Now any number in [M-, M+] (bounds included) will round to w when input, // regardless of how the input rounding algorithm breaks ties. // // And digit_gen generates the shortest possible such number in [M-, M+]. // Note that this does not mean that Grisu2 always generates the shortest // possible number in the interval (m-, m+). const diyfp M_minus(w_minus.f + 1, w_minus.e); const diyfp M_plus (w_plus.f - 1, w_plus.e ); decimal_exponent = -cached.k; // = -(-k) = k grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); } /*! v = buf * 10^decimal_exponent len is the length of the buffer (number of decimal digits) The buffer must be large enough, i.e. >= max_digits10. */ template JSON_HEDLEY_NON_NULL(1) void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) { static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, "internal error: not enough precision"); JSON_ASSERT(std::isfinite(value)); JSON_ASSERT(value > 0); // If the neighbors (and boundaries) of 'value' are always computed for double-precision // numbers, all float's can be recovered using strtod (and strtof). However, the resulting // decimal representations are not exactly "short". // // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) // says "value is converted to a string as if by std::sprintf in the default ("C") locale" // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' // does. // On the other hand, the documentation for 'std::to_chars' requires that "parsing the // representation using the corresponding std::from_chars function recovers value exactly". That // indicates that single precision floating-point numbers should be recovered using // 'std::strtof'. // // NB: If the neighbors are computed for single-precision numbers, there is a single float // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision // value is off by 1 ulp. #if 0 const boundaries w = compute_boundaries(static_cast(value)); #else const boundaries w = compute_boundaries(value); #endif grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); } /*! @brief appends a decimal representation of e to buf @return a pointer to the element following the exponent. @pre -1000 < e < 1000 */ JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_RETURNS_NON_NULL inline char* append_exponent(char* buf, int e) { JSON_ASSERT(e > -1000); JSON_ASSERT(e < 1000); if (e < 0) { e = -e; *buf++ = '-'; } else { *buf++ = '+'; } auto k = static_cast(e); if (k < 10) { // Always print at least two digits in the exponent. // This is for compatibility with printf("%g"). *buf++ = '0'; *buf++ = static_cast('0' + k); } else if (k < 100) { *buf++ = static_cast('0' + k / 10); k %= 10; *buf++ = static_cast('0' + k); } else { *buf++ = static_cast('0' + k / 100); k %= 100; *buf++ = static_cast('0' + k / 10); k %= 10; *buf++ = static_cast('0' + k); } return buf; } /*! @brief prettify v = buf * 10^decimal_exponent If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point notation. Otherwise it will be printed in exponential notation. @pre min_exp < 0 @pre max_exp > 0 */ JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_RETURNS_NON_NULL inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp, int max_exp) { JSON_ASSERT(min_exp < 0); JSON_ASSERT(max_exp > 0); const int k = len; const int n = len + decimal_exponent; // v = buf * 10^(n-k) // k is the length of the buffer (number of decimal digits) // n is the position of the decimal point relative to the start of the buffer. if (k <= n && n <= max_exp) { // digits[000] // len <= max_exp + 2 std::memset(buf + k, '0', static_cast(n) - static_cast(k)); // Make it look like a floating-point number (#362, #378) buf[n + 0] = '.'; buf[n + 1] = '0'; return buf + (static_cast(n) + 2); } if (0 < n && n <= max_exp) { // dig.its // len <= max_digits10 + 1 JSON_ASSERT(k > n); std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); buf[n] = '.'; return buf + (static_cast(k) + 1U); } if (min_exp < n && n <= 0) { // 0.[000]digits // len <= 2 + (-min_exp - 1) + max_digits10 std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); buf[0] = '0'; buf[1] = '.'; std::memset(buf + 2, '0', static_cast(-n)); return buf + (2U + static_cast(-n) + static_cast(k)); } if (k == 1) { // dE+123 // len <= 1 + 5 buf += 1; } else { // d.igitsE+123 // len <= max_digits10 + 1 + 5 std::memmove(buf + 2, buf + 1, static_cast(k) - 1); buf[1] = '.'; buf += 1 + static_cast(k); } *buf++ = 'e'; return append_exponent(buf, n - 1); } } // namespace dtoa_impl /*! @brief generates a decimal representation of the floating-point number value in [first, last). The format of the resulting decimal representation is similar to printf's %g format. Returns an iterator pointing past-the-end of the decimal representation. @note The input number must be finite, i.e. NaN's and Inf's are not supported. @note The buffer must be large enough. @note The result is NOT null-terminated. */ template JSON_HEDLEY_NON_NULL(1, 2) JSON_HEDLEY_RETURNS_NON_NULL char* to_chars(char* first, const char* last, FloatType value) { static_cast(last); // maybe unused - fix warning JSON_ASSERT(std::isfinite(value)); // Use signbit(value) instead of (value < 0) since signbit works for -0. if (std::signbit(value)) { value = -value; *first++ = '-'; } #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif if (value == 0) // +-0 { *first++ = '0'; // Make it look like a floating-point number (#362, #378) *first++ = '.'; *first++ = '0'; return first; } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); // Compute v = buffer * 10^decimal_exponent. // The decimal digits are stored in the buffer, which needs to be interpreted // as an unsigned decimal integer. // len is the length of the buffer, i.e. the number of decimal digits. int len = 0; int decimal_exponent = 0; dtoa_impl::grisu2(first, len, decimal_exponent, value); JSON_ASSERT(len <= std::numeric_limits::max_digits10); // Format the buffer like printf("%.*g", prec, value) constexpr int kMinExp = -4; // Use digits10 here to increase compatibility with version 2. constexpr int kMaxExp = std::numeric_limits::digits10; JSON_ASSERT(last - first >= kMaxExp + 2); JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); } } // namespace detail } // namespace nlohmann // #include // #include // #include // #include // #include // #include namespace nlohmann { namespace detail { /////////////////// // serialization // /////////////////// /// how to treat decoding errors enum class error_handler_t { strict, ///< throw a type_error exception in case of invalid UTF-8 replace, ///< replace invalid UTF-8 sequences with U+FFFD ignore ///< ignore invalid UTF-8 sequences }; template class serializer { using string_t = typename BasicJsonType::string_t; using number_float_t = typename BasicJsonType::number_float_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using binary_char_t = typename BasicJsonType::binary_t::value_type; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; public: /*! @param[in] s output stream to serialize to @param[in] ichar indentation character to use @param[in] error_handler_ how to react on decoding errors */ serializer(output_adapter_t s, const char ichar, error_handler_t error_handler_ = error_handler_t::strict) : o(std::move(s)) , loc(std::localeconv()) , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) , indent_char(ichar) , indent_string(512, indent_char) , error_handler(error_handler_) {} // delete because of pointer members serializer(const serializer&) = delete; serializer& operator=(const serializer&) = delete; serializer(serializer&&) = delete; serializer& operator=(serializer&&) = delete; ~serializer() = default; /*! @brief internal implementation of the serialization function This function is called by the public member function dump and organizes the serialization internally. The indentation level is propagated as additional parameter. In case of arrays and objects, the function is called recursively. - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` - floating-point numbers are converted to a string using `"%g"` format - binary values are serialized as objects containing the subtype and the byte array @param[in] val value to serialize @param[in] pretty_print whether the output shall be pretty-printed @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters in the output are escaped with `\uXXXX` sequences, and the result consists of ASCII characters only. @param[in] indent_step the indent level @param[in] current_indent the current indent level (only used internally) */ void dump(const BasicJsonType& val, const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, const unsigned int current_indent = 0) { switch (val.m_type) { case value_t::object: { if (val.m_value.object->empty()) { o->write_characters("{}", 2); return; } if (pretty_print) { o->write_characters("{\n", 2); // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } // first n-1 elements auto i = val.m_value.object->cbegin(); for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } // last element JSON_ASSERT(i != val.m_value.object->cend()); JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); o->write_character('}'); } else { o->write_character('{'); // first n-1 elements auto i = val.m_value.object->cbegin(); for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element JSON_ASSERT(i != val.m_value.object->cend()); JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character('}'); } return; } case value_t::array: { if (val.m_value.array->empty()) { o->write_characters("[]", 2); return; } if (pretty_print) { o->write_characters("[\n", 2); // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } // first n-1 elements for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { o->write_characters(indent_string.c_str(), new_indent); dump(*i, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } // last element JSON_ASSERT(!val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); o->write_character(']'); } else { o->write_character('['); // first n-1 elements for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { dump(*i, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element JSON_ASSERT(!val.m_value.array->empty()); dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); } return; } case value_t::string: { o->write_character('\"'); dump_escaped(*val.m_value.string, ensure_ascii); o->write_character('\"'); return; } case value_t::binary: { if (pretty_print) { o->write_characters("{\n", 2); // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } o->write_characters(indent_string.c_str(), new_indent); o->write_characters("\"bytes\": [", 10); if (!val.m_value.binary->empty()) { for (auto i = val.m_value.binary->cbegin(); i != val.m_value.binary->cend() - 1; ++i) { dump_integer(*i); o->write_characters(", ", 2); } dump_integer(val.m_value.binary->back()); } o->write_characters("],\n", 3); o->write_characters(indent_string.c_str(), new_indent); o->write_characters("\"subtype\": ", 11); if (val.m_value.binary->has_subtype()) { dump_integer(val.m_value.binary->subtype()); } else { o->write_characters("null", 4); } o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); o->write_character('}'); } else { o->write_characters("{\"bytes\":[", 10); if (!val.m_value.binary->empty()) { for (auto i = val.m_value.binary->cbegin(); i != val.m_value.binary->cend() - 1; ++i) { dump_integer(*i); o->write_character(','); } dump_integer(val.m_value.binary->back()); } o->write_characters("],\"subtype\":", 12); if (val.m_value.binary->has_subtype()) { dump_integer(val.m_value.binary->subtype()); o->write_character('}'); } else { o->write_characters("null}", 5); } } return; } case value_t::boolean: { if (val.m_value.boolean) { o->write_characters("true", 4); } else { o->write_characters("false", 5); } return; } case value_t::number_integer: { dump_integer(val.m_value.number_integer); return; } case value_t::number_unsigned: { dump_integer(val.m_value.number_unsigned); return; } case value_t::number_float: { dump_float(val.m_value.number_float); return; } case value_t::discarded: { o->write_characters("", 11); return; } case value_t::null: { o->write_characters("null", 4); return; } default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } } JSON_PRIVATE_UNLESS_TESTED: /*! @brief dump escaped string Escape a string by replacing certain special characters by a sequence of an escape character (backslash) and another character and other control characters by a sequence of "\u" followed by a four-digit hex representation. The escaped string is written to output stream @a o. @param[in] s the string to escape @param[in] ensure_ascii whether to escape non-ASCII characters with \uXXXX sequences @complexity Linear in the length of string @a s. */ void dump_escaped(const string_t& s, const bool ensure_ascii) { std::uint32_t codepoint{}; std::uint8_t state = UTF8_ACCEPT; std::size_t bytes = 0; // number of bytes written to string_buffer // number of bytes written at the point of the last valid byte std::size_t bytes_after_last_accept = 0; std::size_t undumped_chars = 0; for (std::size_t i = 0; i < s.size(); ++i) { const auto byte = static_cast(s[i]); switch (decode(state, codepoint, byte)) { case UTF8_ACCEPT: // decode found a new code point { switch (codepoint) { case 0x08: // backspace { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'b'; break; } case 0x09: // horizontal tab { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 't'; break; } case 0x0A: // newline { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'n'; break; } case 0x0C: // formfeed { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'f'; break; } case 0x0D: // carriage return { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'r'; break; } case 0x22: // quotation mark { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = '\"'; break; } case 0x5C: // reverse solidus { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = '\\'; break; } default: { // escape control characters (0x00..0x1F) or, if // ensure_ascii parameter is used, non-ASCII characters if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) { if (codepoint <= 0xFFFF) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", static_cast(codepoint)); bytes += 6; } else { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", static_cast(0xD7C0u + (codepoint >> 10u)), static_cast(0xDC00u + (codepoint & 0x3FFu))); bytes += 12; } } else { // copy byte to buffer (all previous bytes // been copied have in default case above) string_buffer[bytes++] = s[i]; } break; } } // write buffer and reset index; there must be 13 bytes // left, as this is the maximal number of bytes to be // written ("\uxxxx\uxxxx\0") for one code point if (string_buffer.size() - bytes < 13) { o->write_characters(string_buffer.data(), bytes); bytes = 0; } // remember the byte position of this accept bytes_after_last_accept = bytes; undumped_chars = 0; break; } case UTF8_REJECT: // decode found invalid UTF-8 byte { switch (error_handler) { case error_handler_t::strict: { std::string sn(9, '\0'); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType())); } case error_handler_t::ignore: case error_handler_t::replace: { // in case we saw this character the first time, we // would like to read it again, because the byte // may be OK for itself, but just not OK for the // previous sequence if (undumped_chars > 0) { --i; } // reset length buffer to the last accepted index; // thus removing/ignoring the invalid characters bytes = bytes_after_last_accept; if (error_handler == error_handler_t::replace) { // add a replacement character if (ensure_ascii) { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'u'; string_buffer[bytes++] = 'f'; string_buffer[bytes++] = 'f'; string_buffer[bytes++] = 'f'; string_buffer[bytes++] = 'd'; } else { string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); } // write buffer and reset index; there must be 13 bytes // left, as this is the maximal number of bytes to be // written ("\uxxxx\uxxxx\0") for one code point if (string_buffer.size() - bytes < 13) { o->write_characters(string_buffer.data(), bytes); bytes = 0; } bytes_after_last_accept = bytes; } undumped_chars = 0; // continue processing the string state = UTF8_ACCEPT; break; } default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } break; } default: // decode found yet incomplete multi-byte code point { if (!ensure_ascii) { // code point will not be escaped - copy byte to buffer string_buffer[bytes++] = s[i]; } ++undumped_chars; break; } } } // we finished processing the string if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) { // write buffer if (bytes > 0) { o->write_characters(string_buffer.data(), bytes); } } else { // we finish reading, but do not accept: string was incomplete switch (error_handler) { case error_handler_t::strict: { std::string sn(9, '\0'); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType())); } case error_handler_t::ignore: { // write all accepted bytes o->write_characters(string_buffer.data(), bytes_after_last_accept); break; } case error_handler_t::replace: { // write all accepted bytes o->write_characters(string_buffer.data(), bytes_after_last_accept); // add a replacement character if (ensure_ascii) { o->write_characters("\\ufffd", 6); } else { o->write_characters("\xEF\xBF\xBD", 3); } break; } default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } } } private: /*! @brief count digits Count the number of decimal (base 10) digits for an input unsigned integer. @param[in] x unsigned integer number to count its digits @return number of decimal digits */ inline unsigned int count_digits(number_unsigned_t x) noexcept { unsigned int n_digits = 1; for (;;) { if (x < 10) { return n_digits; } if (x < 100) { return n_digits + 1; } if (x < 1000) { return n_digits + 2; } if (x < 10000) { return n_digits + 3; } x = x / 10000u; n_digits += 4; } } /*! @brief dump an integer Dump a given integer to output stream @a o. Works internally with @a number_buffer. @param[in] x integer number (signed or unsigned) to dump @tparam NumberType either @a number_integer_t or @a number_unsigned_t */ template < typename NumberType, detail::enable_if_t < std::is_integral::value || std::is_same::value || std::is_same::value || std::is_same::value, int > = 0 > void dump_integer(NumberType x) { static constexpr std::array, 100> digits_to_99 { { {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}}, {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}}, {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}}, {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}}, {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}}, {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}}, {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}}, {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}}, {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}}, {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}}, } }; // special case for "0" if (x == 0) { o->write_character('0'); return; } // use a pointer to fill the buffer auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg) const bool is_negative = std::is_signed::value && !(x >= 0); // see issue #755 number_unsigned_t abs_value; unsigned int n_chars{}; if (is_negative) { *buffer_ptr = '-'; abs_value = remove_sign(static_cast(x)); // account one more byte for the minus sign n_chars = 1 + count_digits(abs_value); } else { abs_value = static_cast(x); n_chars = count_digits(abs_value); } // spare 1 byte for '\0' JSON_ASSERT(n_chars < number_buffer.size() - 1); // jump to the end to generate the string from backward // so we later avoid reversing the result buffer_ptr += n_chars; // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu // See: https://www.youtube.com/watch?v=o4-CwDo2zpg while (abs_value >= 100) { const auto digits_index = static_cast((abs_value % 100)); abs_value /= 100; *(--buffer_ptr) = digits_to_99[digits_index][1]; *(--buffer_ptr) = digits_to_99[digits_index][0]; } if (abs_value >= 10) { const auto digits_index = static_cast(abs_value); *(--buffer_ptr) = digits_to_99[digits_index][1]; *(--buffer_ptr) = digits_to_99[digits_index][0]; } else { *(--buffer_ptr) = static_cast('0' + abs_value); } o->write_characters(number_buffer.data(), n_chars); } /*! @brief dump a floating-point number Dump a given floating-point number to output stream @a o. Works internally with @a number_buffer. @param[in] x floating-point number to dump */ void dump_float(number_float_t x) { // NaN / inf if (!std::isfinite(x)) { o->write_characters("null", 4); return; } // If number_float_t is an IEEE-754 single or double precision number, // use the Grisu2 algorithm to produce short numbers which are // guaranteed to round-trip, using strtof and strtod, resp. // // NB: The test below works if == . static constexpr bool is_ieee_single_or_double = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); dump_float(x, std::integral_constant()); } void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) { auto* begin = number_buffer.data(); auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); o->write_characters(begin, static_cast(end - begin)); } void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) { // get number of digits for a float -> text -> float round-trip static constexpr auto d = std::numeric_limits::max_digits10; // the actual conversion // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); // negative value indicates an error JSON_ASSERT(len > 0); // check if buffer was large enough JSON_ASSERT(static_cast(len) < number_buffer.size()); // erase thousands separator if (thousands_sep != '\0') { // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081 const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); std::fill(end, number_buffer.end(), '\0'); JSON_ASSERT((end - number_buffer.begin()) <= len); len = (end - number_buffer.begin()); } // convert decimal point to '.' if (decimal_point != '\0' && decimal_point != '.') { // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081 const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); if (dec_pos != number_buffer.end()) { *dec_pos = '.'; } } o->write_characters(number_buffer.data(), static_cast(len)); // determine if need to append ".0" const bool value_is_int_like = std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, [](char c) { return c == '.' || c == 'e'; }); if (value_is_int_like) { o->write_characters(".0", 2); } } /*! @brief check whether a string is UTF-8 encoded The function checks each byte of a string whether it is UTF-8 encoded. The result of the check is stored in the @a state parameter. The function must be called initially with state 0 (accept). State 1 means the string must be rejected, because the current byte is not allowed. If the string is completely processed, but the state is non-zero, the string ended prematurely; that is, the last byte indicated more bytes should have followed. @param[in,out] state the state of the decoding @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) @param[in] byte next byte to decode @return new state @note The function has been edited: a std::array is used. @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ */ static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept { static const std::array utf8d = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 } }; JSON_ASSERT(byte < utf8d.size()); const std::uint8_t type = utf8d[byte]; codep = (state != UTF8_ACCEPT) ? (byte & 0x3fu) | (codep << 6u) : (0xFFu >> type) & (byte); std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); JSON_ASSERT(index < 400); state = utf8d[index]; return state; } /* * Overload to make the compiler happy while it is instantiating * dump_integer for number_unsigned_t. * Must never be called. */ number_unsigned_t remove_sign(number_unsigned_t x) { JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE return x; // LCOV_EXCL_LINE } /* * Helper function for dump_integer * * This function takes a negative signed integer and returns its absolute * value as unsigned integer. The plus/minus shuffling is necessary as we can * not directly remove the sign of an arbitrary signed integer as the * absolute values of INT_MIN and INT_MAX are usually not the same. See * #1708 for details. */ inline number_unsigned_t remove_sign(number_integer_t x) noexcept { JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) return static_cast(-(x + 1)) + 1; } private: /// the output of the serializer output_adapter_t o = nullptr; /// a (hopefully) large enough character buffer std::array number_buffer{{}}; /// the locale const std::lconv* loc = nullptr; /// the locale's thousand separator character const char thousands_sep = '\0'; /// the locale's decimal point character const char decimal_point = '\0'; /// string buffer std::array string_buffer{{}}; /// the indentation character const char indent_char; /// the indentation string string_t indent_string; /// error_handler how to react on decoding errors const error_handler_t error_handler; }; } // namespace detail } // namespace nlohmann // #include // #include // #include #include // less #include // initializer_list #include // input_iterator_tag, iterator_traits #include // allocator #include // for out_of_range #include // enable_if, is_convertible #include // pair #include // vector // #include namespace nlohmann { /// ordered_map: a minimal map-like container that preserves insertion order /// for use within nlohmann::basic_json template , class Allocator = std::allocator>> struct ordered_map : std::vector, Allocator> { using key_type = Key; using mapped_type = T; using Container = std::vector, Allocator>; using typename Container::iterator; using typename Container::const_iterator; using typename Container::size_type; using typename Container::value_type; // Explicit constructors instead of `using Container::Container` // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} template ordered_map(It first, It last, const Allocator& alloc = Allocator()) : Container{first, last, alloc} {} ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) : Container{init, alloc} {} std::pair emplace(const key_type& key, T&& t) { for (auto it = this->begin(); it != this->end(); ++it) { if (it->first == key) { return {it, false}; } } Container::emplace_back(key, t); return {--this->end(), true}; } T& operator[](const Key& key) { return emplace(key, T{}).first->second; } const T& operator[](const Key& key) const { return at(key); } T& at(const Key& key) { for (auto it = this->begin(); it != this->end(); ++it) { if (it->first == key) { return it->second; } } JSON_THROW(std::out_of_range("key not found")); } const T& at(const Key& key) const { for (auto it = this->begin(); it != this->end(); ++it) { if (it->first == key) { return it->second; } } JSON_THROW(std::out_of_range("key not found")); } size_type erase(const Key& key) { for (auto it = this->begin(); it != this->end(); ++it) { if (it->first == key) { // Since we cannot move const Keys, re-construct them in place for (auto next = it; ++next != this->end(); ++it) { it->~value_type(); // Destroy but keep allocation new (&*it) value_type{std::move(*next)}; } Container::pop_back(); return 1; } } return 0; } iterator erase(iterator pos) { auto it = pos; // Since we cannot move const Keys, re-construct them in place for (auto next = it; ++next != this->end(); ++it) { it->~value_type(); // Destroy but keep allocation new (&*it) value_type{std::move(*next)}; } Container::pop_back(); return pos; } size_type count(const Key& key) const { for (auto it = this->begin(); it != this->end(); ++it) { if (it->first == key) { return 1; } } return 0; } iterator find(const Key& key) { for (auto it = this->begin(); it != this->end(); ++it) { if (it->first == key) { return it; } } return Container::end(); } const_iterator find(const Key& key) const { for (auto it = this->begin(); it != this->end(); ++it) { if (it->first == key) { return it; } } return Container::end(); } std::pair insert( value_type&& value ) { return emplace(value.first, std::move(value.second)); } std::pair insert( const value_type& value ) { for (auto it = this->begin(); it != this->end(); ++it) { if (it->first == value.first) { return {it, false}; } } Container::push_back(value); return {--this->end(), true}; } template using require_input_iter = typename std::enable_if::iterator_category, std::input_iterator_tag>::value>::type; template> void insert(InputIt first, InputIt last) { for (auto it = first; it != last; ++it) { insert(*it); } } }; } // namespace nlohmann #if defined(JSON_HAS_CPP_17) #include #endif /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @since version 1.0.0 */ namespace nlohmann { /*! @brief a class to store JSON values @tparam ObjectType type for JSON objects (`std::map` by default; will be used in @ref object_t) @tparam ArrayType type for JSON arrays (`std::vector` by default; will be used in @ref array_t) @tparam StringType type for JSON strings and object keys (`std::string` by default; will be used in @ref string_t) @tparam BooleanType type for JSON booleans (`bool` by default; will be used in @ref boolean_t) @tparam NumberIntegerType type for JSON integer numbers (`int64_t` by default; will be used in @ref number_integer_t) @tparam NumberUnsignedType type for JSON unsigned integer numbers (@c `uint64_t` by default; will be used in @ref number_unsigned_t) @tparam NumberFloatType type for JSON floating-point numbers (`double` by default; will be used in @ref number_float_t) @tparam BinaryType type for packed binary data for compatibility with binary serialization formats (`std::vector` by default; will be used in @ref binary_t) @tparam AllocatorType type of the allocator to use (`std::allocator` by default) @tparam JSONSerializer the serializer to resolve internal calls to `to_json()` and `from_json()` (@ref adl_serializer by default) @requirement The class satisfies the following concept requirements: - Basic - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): JSON values can be default constructed. The result will be a JSON null value. - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): A JSON value can be constructed from an rvalue argument. - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): A JSON value can be copy-constructed from an lvalue expression. - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): A JSON value van be assigned from an rvalue argument. - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): A JSON value can be copy-assigned from an lvalue expression. - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): JSON values can be destructed. - Layout - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): JSON values have [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): All non-static data members are private and standard layout types, the class has no virtual functions or (virtual) base classes. - Library-wide - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): JSON values can be compared with `==`, see @ref operator==(const_reference,const_reference). - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): JSON values can be compared with `<`, see @ref operator<(const_reference,const_reference). - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of other compatible types, using unqualified function call @ref swap(). - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): JSON values can be compared against `std::nullptr_t` objects which are used to model the `null` value. - Container - [Container](https://en.cppreference.com/w/cpp/named_req/Container): JSON values can be used like STL containers and provide iterator access. - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer); JSON values can be used like STL containers and provide reverse iterator access. @invariant The member variables @a m_value and @a m_type have the following relationship: - If `m_type == value_t::object`, then `m_value.object != nullptr`. - If `m_type == value_t::array`, then `m_value.array != nullptr`. - If `m_type == value_t::string`, then `m_value.string != nullptr`. The invariants are checked by member function assert_invariant(). @internal @note ObjectType trick from https://stackoverflow.com/a/9860911 @endinternal @see [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc8259) @since version 1.0.0 @nosubgrouping */ NLOHMANN_BASIC_JSON_TPL_DECLARATION class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) { private: template friend struct detail::external_constructor; friend ::nlohmann::json_pointer; template friend class ::nlohmann::detail::parser; friend ::nlohmann::detail::serializer; template friend class ::nlohmann::detail::iter_impl; template friend class ::nlohmann::detail::binary_writer; template friend class ::nlohmann::detail::binary_reader; template friend class ::nlohmann::detail::json_sax_dom_parser; template friend class ::nlohmann::detail::json_sax_dom_callback_parser; friend class ::nlohmann::detail::exception; /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; JSON_PRIVATE_UNLESS_TESTED: // convenience aliases for types residing in namespace detail; using lexer = ::nlohmann::detail::lexer_base; template static ::nlohmann::detail::parser parser( InputAdapterType adapter, detail::parser_callback_tcb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false ) { return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions, ignore_comments); } private: using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; template using internal_iterator = ::nlohmann::detail::internal_iterator; template using iter_impl = ::nlohmann::detail::iter_impl; template using iteration_proxy = ::nlohmann::detail::iteration_proxy; template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; template using output_adapter_t = ::nlohmann::detail::output_adapter_t; template using binary_reader = ::nlohmann::detail::binary_reader; template using binary_writer = ::nlohmann::detail::binary_writer; JSON_PRIVATE_UNLESS_TESTED: using serializer = ::nlohmann::detail::serializer; public: using value_t = detail::value_t; /// JSON Pointer, see @ref nlohmann::json_pointer using json_pointer = ::nlohmann::json_pointer; template using json_serializer = JSONSerializer; /// how to treat decoding errors using error_handler_t = detail::error_handler_t; /// how to treat CBOR tags using cbor_tag_handler_t = detail::cbor_tag_handler_t; /// helper type for initializer lists of basic_json values using initializer_list_t = std::initializer_list>; using input_format_t = detail::input_format_t; /// SAX interface type, see @ref nlohmann::json_sax using json_sax_t = json_sax; //////////////// // exceptions // //////////////// /// @name exceptions /// Classes to implement user-defined exceptions. /// @{ /// @copydoc detail::exception using exception = detail::exception; /// @copydoc detail::parse_error using parse_error = detail::parse_error; /// @copydoc detail::invalid_iterator using invalid_iterator = detail::invalid_iterator; /// @copydoc detail::type_error using type_error = detail::type_error; /// @copydoc detail::out_of_range using out_of_range = detail::out_of_range; /// @copydoc detail::other_error using other_error = detail::other_error; /// @} ///////////////////// // container types // ///////////////////// /// @name container types /// The canonic container types to use @ref basic_json like any other STL /// container. /// @{ /// the type of elements in a basic_json container using value_type = basic_json; /// the type of an element reference using reference = value_type&; /// the type of an element const reference using const_reference = const value_type&; /// a type to represent differences between iterators using difference_type = std::ptrdiff_t; /// a type to represent container sizes using size_type = std::size_t; /// the allocator type using allocator_type = AllocatorType; /// the type of an element pointer using pointer = typename std::allocator_traits::pointer; /// the type of an element const pointer using const_pointer = typename std::allocator_traits::const_pointer; /// an iterator for a basic_json container using iterator = iter_impl; /// a const iterator for a basic_json container using const_iterator = iter_impl; /// a reverse iterator for a basic_json container using reverse_iterator = json_reverse_iterator; /// a const reverse iterator for a basic_json container using const_reverse_iterator = json_reverse_iterator; /// @} /*! @brief returns the allocator associated with the container */ static allocator_type get_allocator() { return allocator_type(); } /*! @brief returns version information on the library This function returns a JSON object with information about the library, including the version number and information on the platform and compiler. @return JSON object holding version information key | description ----------- | --------------- `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). `copyright` | The copyright line for the library as string. `name` | The name of the library as string. `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. `url` | The URL of the project as string. `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). @liveexample{The following code shows an example output of the `meta()` function.,meta} @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @complexity Constant. @since 2.1.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json meta() { basic_json result; result["copyright"] = "(C) 2013-2021 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + std::to_string(NLOHMANN_JSON_VERSION_PATCH); result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; #ifdef _WIN32 result["platform"] = "win32"; #elif defined __linux__ result["platform"] = "linux"; #elif defined __APPLE__ result["platform"] = "apple"; #elif defined __unix__ result["platform"] = "unix"; #else result["platform"] = "unknown"; #endif #if defined(__ICC) || defined(__INTEL_COMPILER) result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; #elif defined(__clang__) result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__GNUC__) || defined(__GNUG__) result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; #elif defined(__HP_cc) || defined(__HP_aCC) result["compiler"] = "hp" #elif defined(__IBMCPP__) result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; #elif defined(_MSC_VER) result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; #elif defined(__PGI) result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; #elif defined(__SUNPRO_CC) result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; #else result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; #endif #ifdef __cplusplus result["compiler"]["c++"] = std::to_string(__cplusplus); #else result["compiler"]["c++"] = "unknown"; #endif return result; } /////////////////////////// // JSON value data types // /////////////////////////// /// @name JSON value data types /// The data types to store a JSON value. These types are derived from /// the template arguments passed to class @ref basic_json. /// @{ #if defined(JSON_HAS_CPP_14) // Use transparent comparator if possible, combined with perfect forwarding // on find() and count() calls prevents unnecessary string construction. using object_comparator_t = std::less<>; #else using object_comparator_t = std::less; #endif /*! @brief a type for an object [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows: > An object is an unordered collection of zero or more name/value pairs, > where a name is a string and a value is a string, number, boolean, null, > object, or array. To store objects in C++, a type is defined by the template parameters described below. @tparam ObjectType the container to store objects (e.g., `std::map` or `std::unordered_map`) @tparam StringType the type of the keys or names (e.g., `std::string`). The comparison function `std::less` is used to order elements inside the container. @tparam AllocatorType the allocator to use for objects (e.g., `std::allocator`) #### Default type With the default values for @a ObjectType (`std::map`), @a StringType (`std::string`), and @a AllocatorType (`std::allocator`), the default value for @a object_t is: @code {.cpp} std::map< std::string, // key_type basic_json, // value_type std::less, // key_compare std::allocator> // allocator_type > @endcode #### Behavior The choice of @a object_t influences the behavior of the JSON class. With the default type, objects have the following behavior: - When all names are unique, objects will be interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings. - When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or `{"key": 2}`. - Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see @ref dump) in this order. For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored and serialized as `{"a": 2, "b": 1}`. - When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be treated as equal. #### Limits [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the object's limit of nesting is not explicitly constrained. However, a maximum depth of nesting may be introduced by the compiler or runtime environment. A theoretical limit can be queried by calling the @ref max_size function of a JSON object. #### Storage Objects are stored as pointers in a @ref basic_json type. That is, for any access to object values, a pointer of type `object_t*` must be dereferenced. @sa see @ref array_t -- type for an array value @since version 1.0.0 @note The order name/value pairs are added to the object is *not* preserved by the library. Therefore, iterating an object may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects. */ using object_t = ObjectType>>; /*! @brief a type for an array [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows: > An array is an ordered sequence of zero or more values. To store objects in C++, a type is defined by the template parameters explained below. @tparam ArrayType container type to store arrays (e.g., `std::vector` or `std::list`) @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) #### Default type With the default values for @a ArrayType (`std::vector`) and @a AllocatorType (`std::allocator`), the default value for @a array_t is: @code {.cpp} std::vector< basic_json, // value_type std::allocator // allocator_type > @endcode #### Limits [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the array's limit of nesting is not explicitly constrained. However, a maximum depth of nesting may be introduced by the compiler or runtime environment. A theoretical limit can be queried by calling the @ref max_size function of a JSON array. #### Storage Arrays are stored as pointers in a @ref basic_json type. That is, for any access to array values, a pointer of type `array_t*` must be dereferenced. @sa see @ref object_t -- type for an object value @since version 1.0.0 */ using array_t = ArrayType>; /*! @brief a type for a string [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows: > A string is a sequence of zero or more Unicode characters. To store objects in C++, a type is defined by the template parameter described below. Unicode values are split by the JSON class into byte-sized characters during deserialization. @tparam StringType the container to store strings (e.g., `std::string`). Note this container is used for keys/names in objects, see @ref object_t. #### Default type With the default values for @a StringType (`std::string`), the default value for @a string_t is: @code {.cpp} std::string @endcode #### Encoding Strings are stored in UTF-8 encoding. Therefore, functions like `std::string::size()` or `std::string::length()` return the number of bytes in the string rather than the number of characters or glyphs. #### String comparison [RFC 8259](https://tools.ietf.org/html/rfc8259) states: > Software implementations are typically required to test names of object > members for equality. Implementations that transform the textual > representation into sequences of Unicode code units and then perform the > comparison numerically, code unit by code unit, are interoperable in the > sense that implementations will agree in all cases on equality or > inequality of two strings. For example, implementations that compare > strings with escaped characters unconverted may incorrectly find that > `"a\\b"` and `"a\u005Cb"` are not equal. This implementation is interoperable as it does compare strings code unit by code unit. #### Storage String values are stored as pointers in a @ref basic_json type. That is, for any access to string values, a pointer of type `string_t*` must be dereferenced. @since version 1.0.0 */ using string_t = StringType; /*! @brief a type for a boolean [RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a type which differentiates the two literals `true` and `false`. To store objects in C++, a type is defined by the template parameter @a BooleanType which chooses the type to use. #### Default type With the default values for @a BooleanType (`bool`), the default value for @a boolean_t is: @code {.cpp} bool @endcode #### Storage Boolean values are stored directly inside a @ref basic_json type. @since version 1.0.0 */ using boolean_t = BooleanType; /*! @brief a type for a number (integer) [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an > optional minus sign, which may be followed by a fraction part and/or an > exponent part. Leading zeros are not allowed. (...) Numeric values that > cannot be represented in the grammar below (such as Infinity and NaN) > are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template parameter @a NumberIntegerType which chooses the type to use. #### Default type With the default values for @a NumberIntegerType (`int64_t`), the default value for @a number_integer_t is: @code {.cpp} int64_t @endcode #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as octal number. Internally, the value will be stored as decimal number. For instance, the C++ integer literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as @ref number_unsigned_t or @ref number_float_t. [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. As this range is a subrange of the exactly supported range [INT64_MIN, INT64_MAX], this class's integer type is interoperable. #### Storage Integer number values are stored directly inside a @ref basic_json type. @sa see @ref number_float_t -- type for number values (floating-point) @sa see @ref number_unsigned_t -- type for number values (unsigned integer) @since version 1.0.0 */ using number_integer_t = NumberIntegerType; /*! @brief a type for a number (unsigned) [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an > optional minus sign, which may be followed by a fraction part and/or an > exponent part. Leading zeros are not allowed. (...) Numeric values that > cannot be represented in the grammar below (such as Infinity and NaN) > are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store unsigned integer numbers in C++, a type is defined by the template parameter @a NumberUnsignedType which chooses the type to use. #### Default type With the default values for @a NumberUnsignedType (`uint64_t`), the default value for @a number_unsigned_t is: @code {.cpp} uint64_t @endcode #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as octal number. Internally, the value will be stored as decimal number. For instance, the C++ integer literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as @ref number_integer_t or @ref number_float_t. [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. As this range is a subrange (when considered in conjunction with the number_integer_t type) of the exactly supported range [0, UINT64_MAX], this class's integer type is interoperable. #### Storage Integer number values are stored directly inside a @ref basic_json type. @sa see @ref number_float_t -- type for number values (floating-point) @sa see @ref number_integer_t -- type for number values (integer) @since version 2.0.0 */ using number_unsigned_t = NumberUnsignedType; /*! @brief a type for a number (floating-point) [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an > optional minus sign, which may be followed by a fraction part and/or an > exponent part. Leading zeros are not allowed. (...) Numeric values that > cannot be represented in the grammar below (such as Infinity and NaN) > are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template parameter @a NumberFloatType which chooses the type to use. #### Default type With the default values for @a NumberFloatType (`double`), the default value for @a number_float_t is: @code {.cpp} double @endcode #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in floating-point literals will be ignored. Internally, the value will be stored as decimal number. For instance, the C++ floating-point literal `01.2` will be serialized to `1.2`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits [RFC 8259](https://tools.ietf.org/html/rfc8259) states: > This specification allows implementations to set limits on the range and > precision of numbers accepted. Since software that implements IEEE > 754-2008 binary64 (double precision) numbers is generally available and > widely used, good interoperability can be achieved by implementations > that expect no more precision or range than these provide, in the sense > that implementations will approximate JSON numbers within the expected > precision. This implementation does exactly follow this approach, as it uses double precision floating-point numbers. Note values smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally and be serialized to `null`. #### Storage Floating-point number values are stored directly inside a @ref basic_json type. @sa see @ref number_integer_t -- type for number values (integer) @sa see @ref number_unsigned_t -- type for number values (unsigned integer) @since version 1.0.0 */ using number_float_t = NumberFloatType; /*! @brief a type for a packed binary type This type is a type designed to carry binary data that appears in various serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and BSON's generic binary subtype. This type is NOT a part of standard JSON and exists solely for compatibility with these binary types. As such, it is simply defined as an ordered sequence of zero or more byte values. Additionally, as an implementation detail, the subtype of the binary data is carried around as a `std::uint8_t`, which is compatible with both of the binary data formats that use binary subtyping, (though the specific numbering is incompatible with each other, and it is up to the user to translate between them). [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type as: > Major type 2: a byte string. The string's length in bytes is represented > following the rules for positive integers (major type 0). [MessagePack's documentation on the bin type family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) describes this type as: > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes > in addition to the size of the byte array. [BSON's specifications](http://bsonspec.org/spec.html) describe several binary types; however, this type is intended to represent the generic binary type which has the description: > Generic binary subtype - This is the most commonly used binary subtype and > should be the 'default' for drivers and tools. None of these impose any limitations on the internal representation other than the basic unit of storage be some type of array whose parts are decomposable into bytes. The default representation of this binary format is a `std::vector`, which is a very common way to represent a byte array in modern C++. #### Default type The default values for @a BinaryType is `std::vector` #### Storage Binary Arrays are stored as pointers in a @ref basic_json type. That is, for any access to array values, a pointer of the type `binary_t*` must be dereferenced. #### Notes on subtypes - CBOR - Binary values are represented as byte strings. Subtypes are serialized as tagged values. - MessagePack - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The subtype is then added as singed 8-bit integer. - If no subtype is given, the bin family (bin8, bin16, bin32) is used. - BSON - If a subtype is given, it is used and added as unsigned 8-bit integer. - If no subtype is given, the generic binary subtype 0x00 is used. @sa see @ref binary -- create a binary array @since version 3.8.0 */ using binary_t = nlohmann::byte_container_with_subtype; /// @} private: /// helper for exception-safe object creation template JSON_HEDLEY_RETURNS_NON_NULL static T* create(Args&& ... args) { AllocatorType alloc; using AllocatorTraits = std::allocator_traits>; auto deleter = [&](T * obj) { AllocatorTraits::deallocate(alloc, obj, 1); }; std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); JSON_ASSERT(obj != nullptr); return obj.release(); } //////////////////////// // JSON value storage // //////////////////////// JSON_PRIVATE_UNLESS_TESTED: /*! @brief a JSON value The actual storage for a JSON value of the @ref basic_json class. This union combines the different storage types for the JSON value types defined in @ref value_t. JSON type | value_t type | used type --------- | --------------- | ------------------------ object | object | pointer to @ref object_t array | array | pointer to @ref array_t string | string | pointer to @ref string_t boolean | boolean | @ref boolean_t number | number_integer | @ref number_integer_t number | number_unsigned | @ref number_unsigned_t number | number_float | @ref number_float_t binary | binary | pointer to @ref binary_t null | null | *no value is stored* @note Variable-length types (objects, arrays, and strings) are stored as pointers. The size of the union should not exceed 64 bits if the default value types are used. @since version 1.0.0 */ union json_value { /// object (stored with pointer to save storage) object_t* object; /// array (stored with pointer to save storage) array_t* array; /// string (stored with pointer to save storage) string_t* string; /// binary (stored with pointer to save storage) binary_t* binary; /// boolean boolean_t boolean; /// number (integer) number_integer_t number_integer; /// number (unsigned integer) number_unsigned_t number_unsigned; /// number (floating-point) number_float_t number_float; /// default constructor (for null values) json_value() = default; /// constructor for booleans json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) json_value(number_integer_t v) noexcept : number_integer(v) {} /// constructor for numbers (unsigned) json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} /// constructor for numbers (floating-point) json_value(number_float_t v) noexcept : number_float(v) {} /// constructor for empty values of a given type json_value(value_t t) { switch (t) { case value_t::object: { object = create(); break; } case value_t::array: { array = create(); break; } case value_t::string: { string = create(""); break; } case value_t::binary: { binary = create(); break; } case value_t::boolean: { boolean = boolean_t(false); break; } case value_t::number_integer: { number_integer = number_integer_t(0); break; } case value_t::number_unsigned: { number_unsigned = number_unsigned_t(0); break; } case value_t::number_float: { number_float = number_float_t(0.0); break; } case value_t::null: { object = nullptr; // silence warning, see #821 break; } case value_t::discarded: default: { object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.4", basic_json())); // LCOV_EXCL_LINE } break; } } } /// constructor for strings json_value(const string_t& value) { string = create(value); } /// constructor for rvalue strings json_value(string_t&& value) { string = create(std::move(value)); } /// constructor for objects json_value(const object_t& value) { object = create(value); } /// constructor for rvalue objects json_value(object_t&& value) { object = create(std::move(value)); } /// constructor for arrays json_value(const array_t& value) { array = create(value); } /// constructor for rvalue arrays json_value(array_t&& value) { array = create(std::move(value)); } /// constructor for binary arrays json_value(const typename binary_t::container_type& value) { binary = create(value); } /// constructor for rvalue binary arrays json_value(typename binary_t::container_type&& value) { binary = create(std::move(value)); } /// constructor for binary arrays (internal type) json_value(const binary_t& value) { binary = create(value); } /// constructor for rvalue binary arrays (internal type) json_value(binary_t&& value) { binary = create(std::move(value)); } void destroy(value_t t) { if (t == value_t::array || t == value_t::object) { // flatten the current json_value to a heap-allocated stack std::vector stack; // move the top-level items to stack if (t == value_t::array) { stack.reserve(array->size()); std::move(array->begin(), array->end(), std::back_inserter(stack)); } else { stack.reserve(object->size()); for (auto&& it : *object) { stack.push_back(std::move(it.second)); } } while (!stack.empty()) { // move the last item to local variable to be processed basic_json current_item(std::move(stack.back())); stack.pop_back(); // if current_item is array/object, move // its children to the stack to be processed later if (current_item.is_array()) { std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack)); current_item.m_value.array->clear(); } else if (current_item.is_object()) { for (auto&& it : *current_item.m_value.object) { stack.push_back(std::move(it.second)); } current_item.m_value.object->clear(); } // it's now safe that current_item get destructed // since it doesn't have any children } } switch (t) { case value_t::object: { AllocatorType alloc; std::allocator_traits::destroy(alloc, object); std::allocator_traits::deallocate(alloc, object, 1); break; } case value_t::array: { AllocatorType alloc; std::allocator_traits::destroy(alloc, array); std::allocator_traits::deallocate(alloc, array, 1); break; } case value_t::string: { AllocatorType alloc; std::allocator_traits::destroy(alloc, string); std::allocator_traits::deallocate(alloc, string, 1); break; } case value_t::binary: { AllocatorType alloc; std::allocator_traits::destroy(alloc, binary); std::allocator_traits::deallocate(alloc, binary, 1); break; } case value_t::null: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::discarded: default: { break; } } } }; private: /*! @brief checks the class invariants This function asserts the class invariants. It needs to be called at the end of every constructor to make sure that created objects respect the invariant. Furthermore, it has to be called each time the type of a JSON value is changed, because the invariant expresses a relationship between @a m_type and @a m_value. Furthermore, the parent relation is checked for arrays and objects: If @a check_parents true and the value is an array or object, then the container's elements must have the current value as parent. @param[in] check_parents whether the parent relation should be checked. The value is true by default and should only be set to false during destruction of objects when the invariant does not need to hold. */ void assert_invariant(bool check_parents = true) const noexcept { JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); #if JSON_DIAGNOSTICS JSON_TRY { // cppcheck-suppress assertWithSideEffect JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) { return j.m_parent == this; })); } JSON_CATCH(...) {} // LCOV_EXCL_LINE #endif static_cast(check_parents); } void set_parents() { #if JSON_DIAGNOSTICS switch (m_type) { case value_t::array: { for (auto& element : *m_value.array) { element.m_parent = this; } break; } case value_t::object: { for (auto& element : *m_value.object) { element.second.m_parent = this; } break; } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: break; } #endif } iterator set_parents(iterator it, typename iterator::difference_type count) { #if JSON_DIAGNOSTICS for (typename iterator::difference_type i = 0; i < count; ++i) { (it + i)->m_parent = this; } #else static_cast(count); #endif return it; } reference set_parent(reference j, std::size_t old_capacity = std::size_t(-1)) { #if JSON_DIAGNOSTICS if (old_capacity != std::size_t(-1)) { // see https://github.com/nlohmann/json/issues/2838 JSON_ASSERT(type() == value_t::array); if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) { // capacity has changed: update all parents set_parents(); return j; } } // ordered_json uses a vector internally, so pointers could have // been invalidated; see https://github.com/nlohmann/json/issues/2962 #ifdef JSON_HEDLEY_MSVC_VERSION #pragma warning(push ) #pragma warning(disable : 4127) // ignore warning to replace if with if constexpr #endif if (detail::is_ordered_map::value) { set_parents(); return j; } #ifdef JSON_HEDLEY_MSVC_VERSION #pragma warning( pop ) #endif j.m_parent = this; #else static_cast(j); static_cast(old_capacity); #endif return j; } public: ////////////////////////// // JSON parser callback // ////////////////////////// /*! @brief parser event types The parser callback distinguishes the following events: - `object_start`: the parser read `{` and started to process a JSON object - `key`: the parser read a key of a value in an object - `object_end`: the parser read `}` and finished processing a JSON object - `array_start`: the parser read `[` and started to process a JSON array - `array_end`: the parser read `]` and finished processing a JSON array - `value`: the parser finished reading a JSON value @image html callback_events.png "Example when certain parse events are triggered" @sa see @ref parser_callback_t for more information and examples */ using parse_event_t = detail::parse_event_t; /*! @brief per-element parser callback type With a parser callback function, the result of parsing a JSON text can be influenced. When passed to @ref parse, it is called on certain events (passed as @ref parse_event_t via parameter @a event) with a set recursion depth @a depth and context JSON value @a parsed. The return value of the callback function is a boolean indicating whether the element that emitted the callback shall be kept or not. We distinguish six scenarios (determined by the event type) in which the callback function can be called. The following table describes the values of the parameters @a depth, @a event, and @a parsed. parameter @a event | description | parameter @a depth | parameter @a parsed ------------------ | ----------- | ------------------ | ------------------- parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value @image html callback_events.png "Example when certain parse events are triggered" Discarding a value (i.e., returning `false`) has different effects depending on the context in which function was called: - Discarded values in structured types are skipped. That is, the parser will behave as if the discarded value was never read. - In case a value outside a structured type is skipped, it is replaced with `null`. This case happens if the top-level element is skipped. @param[in] depth the depth of the recursion during parsing @param[in] event an event of type parse_event_t indicating the context in the callback function has been called @param[in,out] parsed the current intermediate parse result; note that writing to this value has no effect for parse_event_t::key events @return Whether the JSON value which called the function during parsing should be kept (`true`) or not (`false`). In the latter case, it is either skipped completely or replaced by an empty discarded object. @sa see @ref parse for examples @since version 1.0.0 */ using parser_callback_t = detail::parser_callback_t; ////////////////// // constructors // ////////////////// /// @name constructors and destructors /// Constructors of class @ref basic_json, copy/move constructor, copy /// assignment, static functions creating objects, and the destructor. /// @{ /*! @brief create an empty value with a given type Create an empty JSON value with a given type. The value will be default initialized with an empty value which depends on the type: Value type | initial value ----------- | ------------- null | `null` boolean | `false` string | `""` number | `0` object | `{}` array | `[]` binary | empty array @param[in] v the type of the value to create @complexity Constant. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows the constructor for different @ref value_t values,basic_json__value_t} @sa see @ref clear() -- restores the postcondition of this constructor @since version 1.0.0 */ basic_json(const value_t v) : m_type(v), m_value(v) { assert_invariant(); } /*! @brief create a null object Create a `null` JSON value. It either takes a null pointer as parameter (explicitly creating `null`) or no parameter (implicitly creating `null`). The passed null pointer itself is not read -- it is only used to choose the right constructor. @complexity Constant. @exceptionsafety No-throw guarantee: this constructor never throws exceptions. @liveexample{The following code shows the constructor with and without a null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); } /*! @brief create a JSON value This is a "catch all" constructor for all compatible JSON types; that is, types for which a `to_json()` method exists. The constructor forwards the parameter @a val to that method (to `json_serializer::to_json` method with `U = uncvref_t`, to be exact). Template type @a CompatibleType includes, but is not limited to, the following types: - **arrays**: @ref array_t and all kinds of compatible containers such as `std::vector`, `std::deque`, `std::list`, `std::forward_list`, `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, `std::multiset`, and `std::unordered_multiset` with a `value_type` from which a @ref basic_json value can be constructed. - **objects**: @ref object_t and all kinds of compatible associative containers such as `std::map`, `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with a `key_type` compatible to @ref string_t and a `value_type` from which a @ref basic_json value can be constructed. - **strings**: @ref string_t, string literals, and all compatible string containers can be used. - **numbers**: @ref number_integer_t, @ref number_unsigned_t, @ref number_float_t, and all convertible number types such as `int`, `size_t`, `int64_t`, `float` or `double` can be used. - **boolean**: @ref boolean_t / `bool` can be used. - **binary**: @ref binary_t / `std::vector` may be used, unfortunately because string literals cannot be distinguished from binary character arrays by the C++ type system, all types compatible with `const char*` will be directed to the string constructor instead. This is both for backwards compatibility, and due to the fact that a binary type is not a standard JSON type. See the examples below. @tparam CompatibleType a type such that: - @a CompatibleType is not derived from `std::istream`, - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move constructors), - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) - @a CompatibleType is not a @ref basic_json nested type (e.g., @ref json_pointer, @ref iterator, etc ...) - `json_serializer` has a `to_json(basic_json_t&, CompatibleType&&)` method @tparam U = `uncvref_t` @param[in] val the value to be forwarded to the respective constructor @complexity Usually linear in the size of the passed @a val, also depending on the implementation of the called `to_json()` method. @exceptionsafety Depends on the called constructor. For types directly supported by the library (i.e., all types for which no `to_json()` function was provided), strong guarantee holds: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows the constructor with several compatible types.,basic_json__CompatibleType} @since version 2.1.0 */ template < typename CompatibleType, typename U = detail::uncvref_t, detail::enable_if_t < !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) JSONSerializer::to_json(std::declval(), std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); set_parents(); assert_invariant(); } /*! @brief create a JSON value from an existing one This is a constructor for existing @ref basic_json types. It does not hijack copy/move constructors, since the parameter has different template arguments than the current ones. The constructor tries to convert the internal @ref m_value of the parameter. @tparam BasicJsonType a type such that: - @a BasicJsonType is a @ref basic_json type. - @a BasicJsonType has different template arguments than @ref basic_json_t. @param[in] val the @ref basic_json value to be converted. @complexity Usually linear in the size of the passed @a val, also depending on the implementation of the called `to_json()` method. @exceptionsafety Depends on the called constructor. For types directly supported by the library (i.e., all types for which no `to_json()` function was provided), strong guarantee holds: if an exception is thrown, there are no changes to any JSON value. @since version 3.2.0 */ template < typename BasicJsonType, detail::enable_if_t < detail::is_basic_json::value&& !std::is_same::value, int > = 0 > basic_json(const BasicJsonType& val) { using other_boolean_t = typename BasicJsonType::boolean_t; using other_number_float_t = typename BasicJsonType::number_float_t; using other_number_integer_t = typename BasicJsonType::number_integer_t; using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; using other_string_t = typename BasicJsonType::string_t; using other_object_t = typename BasicJsonType::object_t; using other_array_t = typename BasicJsonType::array_t; using other_binary_t = typename BasicJsonType::binary_t; switch (val.type()) { case value_t::boolean: JSONSerializer::to_json(*this, val.template get()); break; case value_t::number_float: JSONSerializer::to_json(*this, val.template get()); break; case value_t::number_integer: JSONSerializer::to_json(*this, val.template get()); break; case value_t::number_unsigned: JSONSerializer::to_json(*this, val.template get()); break; case value_t::string: JSONSerializer::to_json(*this, val.template get_ref()); break; case value_t::object: JSONSerializer::to_json(*this, val.template get_ref()); break; case value_t::array: JSONSerializer::to_json(*this, val.template get_ref()); break; case value_t::binary: JSONSerializer::to_json(*this, val.template get_ref()); break; case value_t::null: *this = nullptr; break; case value_t::discarded: m_type = value_t::discarded; break; default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } set_parents(); assert_invariant(); } /*! @brief create a container (array or object) from an initializer list Creates a JSON value of type array or object from the passed initializer list @a d2Init. In case @a type_deduction is `true` (default), the type of the JSON value to be created is deducted from the initializer list @a d2Init according to the following rules: 1. If the list is empty, an empty JSON object value `{}` is created. 2. If the list consists of pairs whose first element is a string, a JSON object value is created where the first elements of the pairs are treated as keys and the second elements are as values. 3. In all other cases, an array is created. The rules aim to create the best fit between a C++ initializer list and JSON values. The rationale is as follows: 1. The empty initializer list is written as `{}` which is exactly an empty JSON object. 2. C++ has no way of describing mapped types other than to list a list of pairs. As JSON requires that keys must be of type string, rule 2 is the weakest constraint one can pose on initializer lists to interpret them as an object. 3. In all other cases, the initializer list could not be interpreted as JSON object type, so interpreting it as JSON array type is safe. With the rules described above, the following JSON values cannot be expressed by an initializer list: - the empty array (`[]`): use @ref array(initializer_list_t) with an empty initializer list in this case - arrays whose elements satisfy rule 2: use @ref array(initializer_list_t) with the same initializer list in this case @note When used without parentheses around an empty initializer list, @ref basic_json() is called instead of this function, yielding the JSON null value. @param[in] init initializer list with JSON values @param[in] type_deduction internal parameter; when set to `true`, the type of the JSON value is deducted from the initializer list @a d2Init; when set to `false`, the type provided via @a manual_type is forced. This mode is used by the functions @ref array(initializer_list_t) and @ref object(initializer_list_t). @param[in] manual_type internal parameter; when @a type_deduction is set to `false`, the created JSON value will use the provided type (only @ref value_t::array and @ref value_t::object are valid); when @a type_deduction is set to `true`, this parameter has no effect @throw type_error.301 if @a type_deduction is `false`, @a manual_type is `value_t::object`, but @a d2Init contains an element which is not a pair whose first element is a string. In this case, the constructor could not create an object. If @a type_deduction would have be `true`, an array would have been created. See @ref object(initializer_list_t) for an example. @complexity Linear in the size of the initializer list @a d2Init. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The example below shows how JSON values are created from initializer lists.,basic_json__list_init_t} @sa see @ref array(initializer_list_t) -- create a JSON array value from an initializer list @sa see @ref object(initializer_list_t) -- create a JSON object value from an initializer list @since version 1.0.0 */ basic_json(initializer_list_t init, bool type_deduction = true, value_t manual_type = value_t::array) { // check if each element is an array with two elements whose first // element is a string bool is_an_object = std::all_of(init.begin(), init.end(), [](const detail::json_ref& element_ref) { return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); }); // adjust type if type deduction is not wanted if (!type_deduction) { // if array is wanted, do not create an object though possible if (manual_type == value_t::array) { is_an_object = false; } // if object is wanted but impossible, throw an exception if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); } } if (is_an_object) { // the initializer list is a list of pairs -> create object m_type = value_t::object; m_value = value_t::object; for (auto& element_ref : init) { auto element = element_ref.moved_or_copied(); m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); } } else { // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); } set_parents(); assert_invariant(); } /*! @brief explicitly create a binary array (without subtype) Creates a JSON binary array value from a given binary container. Binary values are part of various binary formats, such as CBOR, MessagePack, and BSON. This constructor is used to create a value for serialization to those formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a binary array type, for backwards compatibility and so it does not happen on accident. @param[in] init container containing bytes to use as binary type @return JSON binary array value @complexity Linear in the size of @a d2Init. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json binary(const typename binary_t::container_type& init) { auto res = basic_json(); res.m_type = value_t::binary; res.m_value = init; return res; } /*! @brief explicitly create a binary array (with subtype) Creates a JSON binary array value from a given binary container. Binary values are part of various binary formats, such as CBOR, MessagePack, and BSON. This constructor is used to create a value for serialization to those formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a binary array type, for backwards compatibility and so it does not happen on accident. @param[in] init container containing bytes to use as binary type @param[in] subtype subtype to use in MessagePack and BSON @return JSON binary array value @complexity Linear in the size of @a d2Init. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) { auto res = basic_json(); res.m_type = value_t::binary; res.m_value = binary_t(init, subtype); return res; } /// @copydoc binary(const typename binary_t::container_type&) JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json binary(typename binary_t::container_type&& init) { auto res = basic_json(); res.m_type = value_t::binary; res.m_value = std::move(init); return res; } /// @copydoc binary(const typename binary_t::container_type&, typename binary_t::subtype_type) JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) { auto res = basic_json(); res.m_type = value_t::binary; res.m_value = binary_t(std::move(init), subtype); return res; } /*! @brief explicitly create an array from an initializer list Creates a JSON array value from a given initializer list. That is, given a list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the initializer list is empty, the empty array `[]` is created. @note This function is only needed to express two edge cases that cannot be realized with the initializer list constructor (@ref basic_json(initializer_list_t, bool, value_t)). These cases are: 1. creating an array whose elements are all pairs whose first element is a string -- in this case, the initializer list constructor would create an object, taking the first elements as keys 2. creating an empty array -- passing the empty initializer list to the initializer list constructor yields an empty object @param[in] init initializer list with JSON values to create an array from (optional) @return JSON array value @complexity Linear in the size of @a d2Init. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows an example for the `array` function.,array} @sa see @ref basic_json(initializer_list_t, bool, value_t) -- create a JSON value from an initializer list @sa see @ref object(initializer_list_t) -- create a JSON object value from an initializer list @since version 1.0.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json array(initializer_list_t init = {}) { return basic_json(init, false, value_t::array); } /*! @brief explicitly create an object from an initializer list Creates a JSON object value from a given initializer list. The initializer lists elements must be pairs, and their first elements must be strings. If the initializer list is empty, the empty object `{}` is created. @note This function is only added for symmetry reasons. In contrast to the related function @ref array(initializer_list_t), there are no cases which can only be expressed by this function. That is, any initializer list @a d2Init can also be passed to the initializer list constructor @ref basic_json(initializer_list_t, bool, value_t). @param[in] init initializer list to create an object from (optional) @return JSON object value @throw type_error.301 if @a d2Init is not a list of pairs whose first elements are strings. In this case, no object can be created. When such a value is passed to @ref basic_json(initializer_list_t, bool, value_t), an array would have been created from the passed initializer list @a d2Init. See example below. @complexity Linear in the size of @a d2Init. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows an example for the `object` function.,object} @sa see @ref basic_json(initializer_list_t, bool, value_t) -- create a JSON value from an initializer list @sa see @ref array(initializer_list_t) -- create a JSON array value from an initializer list @since version 1.0.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json object(initializer_list_t init = {}) { return basic_json(init, false, value_t::object); } /*! @brief construct an array with count copies of given value Constructs a JSON array value by creating @a cnt copies of a passed value. In case @a cnt is `0`, an empty array is created. @param[in] cnt the number of JSON copies of @a val to create @param[in] val the JSON value to copy @post `std::distance(begin(),end()) == cnt` holds. @complexity Linear in @a cnt. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows examples for the @ref basic_json(size_type\, const basic_json&) constructor.,basic_json__size_type_basic_json} @since version 1.0.0 */ basic_json(size_type cnt, const basic_json& val) : m_type(value_t::array) { m_value.array = create(cnt, val); set_parents(); assert_invariant(); } /*! @brief construct a JSON container given an iterator range Constructs the JSON value with the contents of the range `[first, last)`. The semantics depends on the different types a JSON value can have: - In case of a null type, invalid_iterator.206 is thrown. - In case of other primitive types (number, boolean, or string), @a first must be `begin()` and @a last must be `end()`. In this case, the value is copied. Otherwise, invalid_iterator.204 is thrown. - In case of structured types (array, object), the constructor behaves as similar versions for `std::vector` or `std::map`; that is, a JSON array or object is constructed from the values in the range. @tparam InputIT an input iterator type (@ref iterator or @ref const_iterator) @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) @pre Iterators @a first and @a last must be initialized. **This precondition is enforced with an assertion (see warning).** If assertions are switched off, a violation of this precondition yields undefined behavior. @pre Range `[first, last)` is valid. Usually, this precondition cannot be checked efficiently. Only certain edge cases are detected; see the description of the exceptions below. A violation of this precondition yields undefined behavior. @warning A precondition is enforced with a runtime assertion that will result in calling `std::abort` if this precondition is not met. Assertions can be disabled by defining `NDEBUG` at compile time. See https://en.cppreference.com/w/cpp/error/assert for more information. @throw invalid_iterator.201 if iterators @a first and @a last are not compatible (i.e., do not belong to the same JSON value). In this case, the range `[first, last)` is undefined. @throw invalid_iterator.204 if iterators @a first and @a last belong to a primitive type (number, boolean, or string), but @a first does not point to the first element any more. In this case, the range `[first, last)` is undefined. See example code below. @throw invalid_iterator.206 if iterators @a first and @a last belong to a null value. In this case, the range `[first, last)` is undefined. @complexity Linear in distance between @a first and @a last. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The example below shows several ways to create JSON values by specifying a subrange with iterators.,basic_json__InputIt_InputIt} @since version 1.0.0 */ template < class InputIT, typename std::enable_if < std::is_same::value || std::is_same::value, int >::type = 0 > basic_json(InputIT first, InputIT last) { JSON_ASSERT(first.m_object != nullptr); JSON_ASSERT(last.m_object != nullptr); // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); } // copy type from first iterator m_type = first.m_object->m_type; // check if iterator range is complete for primitive values switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::number_unsigned: case value_t::string: { if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); } break; } case value_t::null: case value_t::object: case value_t::array: case value_t::binary: case value_t::discarded: default: break; } switch (m_type) { case value_t::number_integer: { m_value.number_integer = first.m_object->m_value.number_integer; break; } case value_t::number_unsigned: { m_value.number_unsigned = first.m_object->m_value.number_unsigned; break; } case value_t::number_float: { m_value.number_float = first.m_object->m_value.number_float; break; } case value_t::boolean: { m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { m_value = *first.m_object->m_value.string; break; } case value_t::object: { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); break; } case value_t::array: { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); break; } case value_t::binary: { m_value = *first.m_object->m_value.binary; break; } case value_t::null: case value_t::discarded: default: JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); } set_parents(); assert_invariant(); } /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// template, std::is_same>::value, int> = 0 > basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} /*! @brief copy constructor Creates a copy of a given JSON value. @param[in] other the JSON value to copy @post `*this == other` @complexity Linear in the size of @a other. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is linear. - As postcondition, it holds: `other == basic_json(other)`. @liveexample{The following code shows an example for the copy constructor.,basic_json__basic_json} @since version 1.0.0 */ basic_json(const basic_json& other) : m_type(other.m_type) { // check of passed value is valid other.assert_invariant(); switch (m_type) { case value_t::object: { m_value = *other.m_value.object; break; } case value_t::array: { m_value = *other.m_value.array; break; } case value_t::string: { m_value = *other.m_value.string; break; } case value_t::boolean: { m_value = other.m_value.boolean; break; } case value_t::number_integer: { m_value = other.m_value.number_integer; break; } case value_t::number_unsigned: { m_value = other.m_value.number_unsigned; break; } case value_t::number_float: { m_value = other.m_value.number_float; break; } case value_t::binary: { m_value = *other.m_value.binary; break; } case value_t::null: case value_t::discarded: default: break; } set_parents(); assert_invariant(); } /*! @brief move constructor Move constructor. Constructs a JSON value with the contents of the given value @a other using move semantics. It "steals" the resources from @a other and leaves it as JSON null value. @param[in,out] other value to move to this object @post `*this` has the same value as @a other before the call. @post @a other is a JSON null value. @complexity Constant. @exceptionsafety No-throw guarantee: this constructor never throws exceptions. @requirement This function helps `basic_json` satisfying the [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible) requirements. @liveexample{The code below shows the move constructor explicitly called via std::move.,basic_json__moveconstructor} @since version 1.0.0 */ basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { // check that passed value is valid other.assert_invariant(false); // invalidate payload other.m_type = value_t::null; other.m_value = {}; set_parents(); assert_invariant(); } /*! @brief copy assignment Copy assignment operator. Copies a JSON value via the "copy and swap" strategy: It is expressed in terms of the copy constructor, destructor, and the `swap()` member function. @param[in] other value to copy from @complexity Linear. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is linear. @liveexample{The code below shows and example for the copy assignment. It creates a copy of value `a` which is then swapped with `b`. Finally\, the copy of `a` (which is the null value after the swap) is destroyed.,basic_json__copyassignment} @since version 1.0.0 */ basic_json& operator=(basic_json other) noexcept ( std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value&& std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value ) { // check that passed value is valid other.assert_invariant(); using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); set_parents(); assert_invariant(); return *this; } /*! @brief destructor Destroys the JSON value and frees all allocated memory. @complexity Linear. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is linear. - All stored elements are destroyed and all memory is freed. @since version 1.0.0 */ ~basic_json() noexcept { assert_invariant(false); m_value.destroy(m_type); } /// @} public: /////////////////////// // object inspection // /////////////////////// /// @name object inspection /// Functions to inspect the type of a JSON value. /// @{ /*! @brief serialization Serialization function for JSON values. The function tries to mimic Python's `json.dumps()` function, and currently supports its @a indent and @a ensure_ascii parameters. @param[in] indent If indent is nonnegative, then array elements and object members will be pretty-printed with that indent level. An indent level of `0` will only insert newlines. `-1` (the default) selects the most compact representation. @param[in] indent_char The character to use for indentation if @a indent is greater than `0`. The default is ` ` (space). @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters in the output are escaped with `\uXXXX` sequences, and the result consists of ASCII characters only. @param[in] error_handler how to react on decoding errors; there are three possible values: `strict` (throws and exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), and `ignore` (ignore invalid UTF-8 sequences during serialization; all bytes are copied to the output unchanged). @return string containing the serialization of the JSON value @throw type_error.316 if a string stored inside the JSON value is not UTF-8 encoded and @a error_handler is set to strict @note Binary values are serialized as object containing two keys: - "bytes": an array of bytes as integers - "subtype": the subtype as integer or "null" if the binary has no subtype @complexity Linear. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @liveexample{The following example shows the effect of different @a indent\, @a indent_char\, and @a ensure_ascii parameters to the result of the serialization.,dump} @see https://docs.python.org/2/library/json.html#json.dump @since version 1.0.0; indentation character @a indent_char, option @a ensure_ascii and exceptions added in version 3.0.0; error handlers added in version 3.4.0; serialization of binary values added in version 3.8.0. */ string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, const error_handler_t error_handler = error_handler_t::strict) const { string_t result; serializer s(detail::output_adapter(result), indent_char, error_handler); if (indent >= 0) { s.dump(*this, true, ensure_ascii, static_cast(indent)); } else { s.dump(*this, false, ensure_ascii, 0); } return result; } /*! @brief return the type of the JSON value (explicit) Return the type of the JSON value as a value from the @ref value_t enumeration. @return the type of the JSON value Value type | return value ------------------------- | ------------------------- null | value_t::null boolean | value_t::boolean string | value_t::string number (integer) | value_t::number_integer number (unsigned integer) | value_t::number_unsigned number (floating-point) | value_t::number_float object | value_t::object array | value_t::array binary | value_t::binary discarded | value_t::discarded @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `type()` for all JSON types.,type} @sa see @ref operator value_t() -- return the type of the JSON value (implicit) @sa see @ref type_name() -- return the type as string @since version 1.0.0 */ constexpr value_t type() const noexcept { return m_type; } /*! @brief return whether type is primitive This function returns true if and only if the JSON type is primitive (string, number, boolean, or null). @return `true` if type is primitive (string, number, boolean, or null), `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_primitive()` for all JSON types.,is_primitive} @sa see @ref is_structured() -- returns whether JSON value is structured @sa see @ref is_null() -- returns whether JSON value is `null` @sa see @ref is_string() -- returns whether JSON value is a string @sa see @ref is_boolean() -- returns whether JSON value is a boolean @sa see @ref is_number() -- returns whether JSON value is a number @sa see @ref is_binary() -- returns whether JSON value is a binary array @since version 1.0.0 */ constexpr bool is_primitive() const noexcept { return is_null() || is_string() || is_boolean() || is_number() || is_binary(); } /*! @brief return whether type is structured This function returns true if and only if the JSON type is structured (array or object). @return `true` if type is structured (array or object), `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_structured()` for all JSON types.,is_structured} @sa see @ref is_primitive() -- returns whether value is primitive @sa see @ref is_array() -- returns whether value is an array @sa see @ref is_object() -- returns whether value is an object @since version 1.0.0 */ constexpr bool is_structured() const noexcept { return is_array() || is_object(); } /*! @brief return whether value is null This function returns true if and only if the JSON value is null. @return `true` if type is null, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_null()` for all JSON types.,is_null} @since version 1.0.0 */ constexpr bool is_null() const noexcept { return m_type == value_t::null; } /*! @brief return whether value is a boolean This function returns true if and only if the JSON value is a boolean. @return `true` if type is boolean, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_boolean()` for all JSON types.,is_boolean} @since version 1.0.0 */ constexpr bool is_boolean() const noexcept { return m_type == value_t::boolean; } /*! @brief return whether value is a number This function returns true if and only if the JSON value is a number. This includes both integer (signed and unsigned) and floating-point values. @return `true` if type is number (regardless whether integer, unsigned integer or floating-type), `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_number()` for all JSON types.,is_number} @sa see @ref is_number_integer() -- check if value is an integer or unsigned integer number @sa see @ref is_number_unsigned() -- check if value is an unsigned integer number @sa see @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ constexpr bool is_number() const noexcept { return is_number_integer() || is_number_float(); } /*! @brief return whether value is an integer number This function returns true if and only if the JSON value is a signed or unsigned integer number. This excludes floating-point values. @return `true` if type is an integer or unsigned integer number, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_number_integer()` for all JSON types.,is_number_integer} @sa see @ref is_number() -- check if value is a number @sa see @ref is_number_unsigned() -- check if value is an unsigned integer number @sa see @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ constexpr bool is_number_integer() const noexcept { return m_type == value_t::number_integer || m_type == value_t::number_unsigned; } /*! @brief return whether value is an unsigned integer number This function returns true if and only if the JSON value is an unsigned integer number. This excludes floating-point and signed integer values. @return `true` if type is an unsigned integer number, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_number_unsigned()` for all JSON types.,is_number_unsigned} @sa see @ref is_number() -- check if value is a number @sa see @ref is_number_integer() -- check if value is an integer or unsigned integer number @sa see @ref is_number_float() -- check if value is a floating-point number @since version 2.0.0 */ constexpr bool is_number_unsigned() const noexcept { return m_type == value_t::number_unsigned; } /*! @brief return whether value is a floating-point number This function returns true if and only if the JSON value is a floating-point number. This excludes signed and unsigned integer values. @return `true` if type is a floating-point number, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_number_float()` for all JSON types.,is_number_float} @sa see @ref is_number() -- check if value is number @sa see @ref is_number_integer() -- check if value is an integer number @sa see @ref is_number_unsigned() -- check if value is an unsigned integer number @since version 1.0.0 */ constexpr bool is_number_float() const noexcept { return m_type == value_t::number_float; } /*! @brief return whether value is an object This function returns true if and only if the JSON value is an object. @return `true` if type is object, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_object()` for all JSON types.,is_object} @since version 1.0.0 */ constexpr bool is_object() const noexcept { return m_type == value_t::object; } /*! @brief return whether value is an array This function returns true if and only if the JSON value is an array. @return `true` if type is array, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_array()` for all JSON types.,is_array} @since version 1.0.0 */ constexpr bool is_array() const noexcept { return m_type == value_t::array; } /*! @brief return whether value is a string This function returns true if and only if the JSON value is a string. @return `true` if type is string, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_string()` for all JSON types.,is_string} @since version 1.0.0 */ constexpr bool is_string() const noexcept { return m_type == value_t::string; } /*! @brief return whether value is a binary array This function returns true if and only if the JSON value is a binary array. @return `true` if type is binary array, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_binary()` for all JSON types.,is_binary} @since version 3.8.0 */ constexpr bool is_binary() const noexcept { return m_type == value_t::binary; } /*! @brief return whether value is discarded This function returns true if and only if the JSON value was discarded during parsing with a callback function (see @ref parser_callback_t). @note This function will always be `false` for JSON values after parsing. That is, discarded values can only occur during parsing, but will be removed when inside a structured value or replaced by null in other cases. @return `true` if type is discarded, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_discarded()` for all JSON types.,is_discarded} @since version 1.0.0 */ constexpr bool is_discarded() const noexcept { return m_type == value_t::discarded; } /*! @brief return the type of the JSON value (implicit) Implicitly return the type of the JSON value as a value from the @ref value_t enumeration. @return the type of the JSON value @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies the @ref value_t operator for all JSON types.,operator__value_t} @sa see @ref type() -- return the type of the JSON value (explicit) @sa see @ref type_name() -- return the type as string @since version 1.0.0 */ constexpr operator value_t() const noexcept { return m_type; } /// @} private: ////////////////// // value access // ////////////////// /// get a boolean (explicit) boolean_t get_impl(boolean_t* /*unused*/) const { if (JSON_HEDLEY_LIKELY(is_boolean())) { return m_value.boolean; } JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); } /// get a pointer to the value (object) object_t* get_impl_ptr(object_t* /*unused*/) noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (object) constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (array) array_t* get_impl_ptr(array_t* /*unused*/) noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (array) constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (string) string_t* get_impl_ptr(string_t* /*unused*/) noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (string) constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (boolean) boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (boolean) constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (integer number) number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (unsigned number) number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (unsigned number) constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (floating-point number) number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (floating-point number) constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (binary) binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept { return is_binary() ? m_value.binary : nullptr; } /// get a pointer to the value (binary) constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept { return is_binary() ? m_value.binary : nullptr; } /*! @brief helper function to implement get_ref() This function helps to implement get_ref() without code duplication for const and non-const overloads @tparam ThisType will be deduced as `basic_json` or `const basic_json` @throw type_error.303 if ReferenceType does not match underlying value type of the current JSON */ template static ReferenceType get_ref_impl(ThisType& obj) { // delegate the call to get_ptr<>() auto* ptr = obj.template get_ptr::type>(); if (JSON_HEDLEY_LIKELY(ptr != nullptr)) { return *ptr; } JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); } public: /// @name value access /// Direct access to the stored value of a JSON value. /// @{ /*! @brief get a pointer value (implicit) Implicit pointer access to the internally stored JSON value. No copies are made. @warning Writing data to the pointee of the result yields an undefined state. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @ref number_unsigned_t, or @ref number_float_t. Enforced by a static assertion. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @complexity Constant. @liveexample{The example below shows how pointers to internal values of a JSON value can be requested. Note that no type conversions are made and a `nullptr` is returned if the value and the requested pointer type does not match.,get_ptr} @since version 1.0.0 */ template::value, int>::type = 0> auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { // delegate the call to get_impl_ptr<>() return get_impl_ptr(static_cast(nullptr)); } /*! @brief get a pointer value (implicit) @copydoc get_ptr() */ template < typename PointerType, typename std::enable_if < std::is_pointer::value&& std::is_const::type>::value, int >::type = 0 > constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { // delegate the call to get_impl_ptr<>() const return get_impl_ptr(static_cast(nullptr)); } private: /*! @brief get a value (explicit) Explicit type conversion between the JSON value and a compatible value which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). The value is converted by calling the @ref json_serializer `from_json()` method. The function is equivalent to executing @code {.cpp} ValueType ret; JSONSerializer::from_json(*this, ret); return ret; @endcode This overloads is chosen if: - @a ValueType is not @ref basic_json, - @ref json_serializer has a `from_json()` method of the form `void from_json(const basic_json&, ValueType&)`, and - @ref json_serializer does not have a `from_json()` method of the form `ValueType from_json(const basic_json&)` @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @throw what @ref json_serializer `from_json()` method throws @liveexample{The example below shows several conversions from JSON values to other types. There a few things to note: (1) Floating-point numbers can be converted to integers\, (2) A JSON array can be converted to a standard `std::vector`\, (3) A JSON object can be converted to C++ associative containers such as `std::unordered_map`.,get__ValueType_const} @since version 2.1.0 */ template < typename ValueType, detail::enable_if_t < detail::is_default_constructible::value&& detail::has_from_json::value, int > = 0 > ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( JSONSerializer::from_json(std::declval(), std::declval()))) { auto ret = ValueType(); JSONSerializer::from_json(*this, ret); return ret; } /*! @brief get a value (explicit); special case Explicit type conversion between the JSON value and a compatible value which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). The value is converted by calling the @ref json_serializer `from_json()` method. The function is equivalent to executing @code {.cpp} return JSONSerializer::from_json(*this); @endcode This overloads is chosen if: - @a ValueType is not @ref basic_json and - @ref json_serializer has a `from_json()` method of the form `ValueType from_json(const basic_json&)` @note If @ref json_serializer has both overloads of `from_json()`, this one is chosen. @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @throw what @ref json_serializer `from_json()` method throws @since version 2.1.0 */ template < typename ValueType, detail::enable_if_t < detail::has_non_default_from_json::value, int > = 0 > ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( JSONSerializer::from_json(std::declval()))) { return JSONSerializer::from_json(*this); } /*! @brief get special-case overload This overloads converts the current @ref basic_json in a different @ref basic_json type @tparam BasicJsonType == @ref basic_json @return a copy of *this, converted into @a BasicJsonType @complexity Depending on the implementation of the called `from_json()` method. @since version 3.2.0 */ template < typename BasicJsonType, detail::enable_if_t < detail::is_basic_json::value, int > = 0 > BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const { return *this; } /*! @brief get special-case overload This overloads avoids a lot of template boilerplate, it can be seen as the identity method @tparam BasicJsonType == @ref basic_json @return a copy of *this @complexity Constant. @since version 2.1.0 */ template::value, int> = 0> basic_json get_impl(detail::priority_tag<3> /*unused*/) const { return *this; } /*! @brief get a pointer value (explicit) @copydoc get() */ template::value, int> = 0> constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept -> decltype(std::declval().template get_ptr()) { // delegate the call to get_ptr return get_ptr(); } public: /*! @brief get a (pointer) value (explicit) Performs explicit type conversion between the JSON value and a compatible value if required. - If the requested type is a pointer to the internally stored JSON value that pointer is returned. No copies are made. - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible from the current @ref basic_json. - Otherwise the value is converted by calling the @ref json_serializer `from_json()` method. @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @tparam ValueType if necessary @throw what @ref json_serializer `from_json()` method throws if conversion is required @since version 2.1.0 */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> #if defined(JSON_HAS_CPP_14) constexpr #endif auto get() const noexcept( noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) { // we cannot static_assert on ValueTypeCV being non-const, because // there is support for get(), which is why we // still need the uncvref static_assert(!std::is_reference::value, "get() cannot be used with reference types, you might want to use get_ref()"); return get_impl(detail::priority_tag<4> {}); } /*! @brief get a pointer value (explicit) Explicit pointer access to the internally stored JSON value. No copies are made. @warning The pointer becomes invalid if the underlying JSON object changes. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @complexity Constant. @liveexample{The example below shows how pointers to internal values of a JSON value can be requested. Note that no type conversions are made and a `nullptr` is returned if the value and the requested pointer type does not match.,get__PointerType} @sa see @ref get_ptr() for explicit pointer-member access @since version 1.0.0 */ template::value, int>::type = 0> auto get() noexcept -> decltype(std::declval().template get_ptr()) { // delegate the call to get_ptr return get_ptr(); } /*! @brief get a value (explicit) Explicit type conversion between the JSON value and a compatible value. The value is filled into the input parameter by calling the @ref json_serializer `from_json()` method. The function is equivalent to executing @code {.cpp} ValueType v; JSONSerializer::from_json(*this, v); @endcode This overloads is chosen if: - @a ValueType is not @ref basic_json, - @ref json_serializer has a `from_json()` method of the form `void from_json(const basic_json&, ValueType&)`, and @tparam ValueType the input parameter type. @return the input parameter, allowing chaining calls. @throw what @ref json_serializer `from_json()` method throws @liveexample{The example below shows several conversions from JSON values to other types. There a few things to note: (1) Floating-point numbers can be converted to integers\, (2) A JSON array can be converted to a standard `std::vector`\, (3) A JSON object can be converted to C++ associative containers such as `std::unordered_map`.,get_to} @since version 3.3.0 */ template < typename ValueType, detail::enable_if_t < !detail::is_basic_json::value&& detail::has_from_json::value, int > = 0 > ValueType & get_to(ValueType& v) const noexcept(noexcept( JSONSerializer::from_json(std::declval(), v))) { JSONSerializer::from_json(*this, v); return v; } // specialization to allow to call get_to with a basic_json value // see https://github.com/nlohmann/json/issues/2175 template::value, int> = 0> ValueType & get_to(ValueType& v) const { v = *this; return v; } template < typename T, std::size_t N, typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) detail::enable_if_t < detail::has_from_json::value, int > = 0 > Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) noexcept(noexcept(JSONSerializer::from_json( std::declval(), v))) { JSONSerializer::from_json(*this, v); return v; } /*! @brief get a reference value (implicit) Implicit reference access to the internally stored JSON value. No copies are made. @warning Writing data to the referee of the result yields an undefined state. @tparam ReferenceType reference type; must be a reference to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref number_float_t. Enforced by static assertion. @return reference to the internally stored JSON value if the requested reference type @a ReferenceType fits to the JSON value; throws type_error.303 otherwise @throw type_error.303 in case passed type @a ReferenceType is incompatible with the stored JSON value; see example below @complexity Constant. @liveexample{The example shows several calls to `get_ref()`.,get_ref} @since version 1.1.0 */ template::value, int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl return get_ref_impl(*this); } /*! @brief get a reference value (implicit) @copydoc get_ref() */ template < typename ReferenceType, typename std::enable_if < std::is_reference::value&& std::is_const::type>::value, int >::type = 0 > ReferenceType get_ref() const { // delegate call to get_ref_impl return get_ref_impl(*this); } /*! @brief get a value (implicit) Implicit type conversion between the JSON value and a compatible value. The call is realized by calling @ref get() const. @tparam ValueType non-pointer type compatible to the JSON value, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays. The character type of @ref string_t as well as an initializer list of this type is excluded to avoid ambiguities as these types implicitly convert to `std::string`. @return copy of the JSON value, converted to type @a ValueType @throw type_error.302 in case passed type @a ValueType is incompatible to the JSON value type (e.g., the JSON value is of type boolean, but a string is requested); see example below @complexity Linear in the size of the JSON value. @liveexample{The example below shows several conversions from JSON values to other types. There a few things to note: (1) Floating-point numbers can be converted to integers\, (2) A JSON array can be converted to a standard `std::vector`\, (3) A JSON object can be converted to C++ associative containers such as `std::unordered_map`.,operator__ValueType} @since version 1.0.0 */ template < typename ValueType, typename std::enable_if < detail::conjunction < detail::negation>, detail::negation>>, detail::negation>, detail::negation>, detail::negation>>, #if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) detail::negation>, #endif detail::is_detected_lazy >::value, int >::type = 0 > JSON_EXPLICIT operator ValueType() const { // delegate the call to get<>() const return get(); } /*! @return reference to the binary value @throw type_error.302 if the value is not binary @sa see @ref is_binary() to check if the value is binary @since version 3.8.0 */ binary_t& get_binary() { if (!is_binary()) { JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); } return *get_ptr(); } /// @copydoc get_binary() const binary_t& get_binary() const { if (!is_binary()) { JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); } return *get_ptr(); } /// @} //////////////////// // element access // //////////////////// /// @name element access /// Access to the JSON value. /// @{ /*! @brief access specified array element with bounds checking Returns a reference to the element at specified location @a idx, with bounds checking. @param[in] idx index of the element to access @return reference to the element at index @a idx @throw type_error.304 if the JSON value is not an array; in this case, calling `at` with an index makes no sense. See example below. @throw out_of_range.401 if the index @a idx is out of range of the array; that is, `idx >= size()`. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 1.0.0 @liveexample{The example below shows how array elements can be read and written using `at()`. It also demonstrates the different exceptions that can be thrown.,at__size_type} */ reference at(size_type idx) { // at only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { JSON_TRY { return set_parent(m_value.array->at(idx)); } JSON_CATCH (std::out_of_range&) { // create better exception explanation JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } } else { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } /*! @brief access specified array element with bounds checking Returns a const reference to the element at specified location @a idx, with bounds checking. @param[in] idx index of the element to access @return const reference to the element at index @a idx @throw type_error.304 if the JSON value is not an array; in this case, calling `at` with an index makes no sense. See example below. @throw out_of_range.401 if the index @a idx is out of range of the array; that is, `idx >= size()`. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 1.0.0 @liveexample{The example below shows how array elements can be read using `at()`. It also demonstrates the different exceptions that can be thrown., at__size_type_const} */ const_reference at(size_type idx) const { // at only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { JSON_TRY { return m_value.array->at(idx); } JSON_CATCH (std::out_of_range&) { // create better exception explanation JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } } else { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } /*! @brief access specified object element with bounds checking Returns a reference to the element at with specified key @a key, with bounds checking. @param[in] key key of the element to access @return reference to the element at key @a key @throw type_error.304 if the JSON value is not an object; in this case, calling `at` with a key makes no sense. See example below. @throw out_of_range.403 if the key @a key is is not stored in the object; that is, `find(key) == end()`. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Logarithmic in the size of the container. @sa see @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa see @ref value() for access by value with a default value @since version 1.0.0 @liveexample{The example below shows how object elements can be read and written using `at()`. It also demonstrates the different exceptions that can be thrown.,at__object_t_key_type} */ reference at(const typename object_t::key_type& key) { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { JSON_TRY { return set_parent(m_value.object->at(key)); } JSON_CATCH (std::out_of_range&) { // create better exception explanation JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); } } else { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } /*! @brief access specified object element with bounds checking Returns a const reference to the element at with specified key @a key, with bounds checking. @param[in] key key of the element to access @return const reference to the element at key @a key @throw type_error.304 if the JSON value is not an object; in this case, calling `at` with a key makes no sense. See example below. @throw out_of_range.403 if the key @a key is is not stored in the object; that is, `find(key) == end()`. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Logarithmic in the size of the container. @sa see @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa see @ref value() for access by value with a default value @since version 1.0.0 @liveexample{The example below shows how object elements can be read using `at()`. It also demonstrates the different exceptions that can be thrown., at__object_t_key_type_const} */ const_reference at(const typename object_t::key_type& key) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { JSON_TRY { return m_value.object->at(key); } JSON_CATCH (std::out_of_range&) { // create better exception explanation JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); } } else { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } /*! @brief access specified array element Returns a reference to the element at specified location @a idx. @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), then the array is silently filled up with `null` values to make `idx` a valid reference to the last stored element. @param[in] idx index of the element to access @return reference to the element at index @a idx @throw type_error.305 if the JSON value is not an array or null; in that cases, using the [] operator with an index makes no sense. @complexity Constant if @a idx is in the range of the array. Otherwise linear in `idx - size()`. @liveexample{The example below shows how array elements can be read and written using `[]` operator. Note the addition of `null` values.,operatorarray__size_type} @since version 1.0.0 */ reference operator[](size_type idx) { // implicitly convert null value to an empty array if (is_null()) { m_type = value_t::array; m_value.array = create(); assert_invariant(); } // operator[] only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) { #if JSON_DIAGNOSTICS // remember array size & capacity before resizing const auto old_size = m_value.array->size(); const auto old_capacity = m_value.array->capacity(); #endif m_value.array->resize(idx + 1); #if JSON_DIAGNOSTICS if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) { // capacity has changed: update all parents set_parents(); } else { // set parent for values added above set_parents(begin() + static_cast(old_size), static_cast(idx + 1 - old_size)); } #endif assert_invariant(); } return m_value.array->operator[](idx); } JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); } /*! @brief access specified array element Returns a const reference to the element at specified location @a idx. @param[in] idx index of the element to access @return const reference to the element at index @a idx @throw type_error.305 if the JSON value is not an array; in that case, using the [] operator with an index makes no sense. @complexity Constant. @liveexample{The example below shows how array elements can be read using the `[]` operator.,operatorarray__size_type_const} @since version 1.0.0 */ const_reference operator[](size_type idx) const { // const operator[] only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { return m_value.array->operator[](idx); } JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); } /*! @brief access specified object element Returns a reference to the element at with specified key @a key. @note If @a key is not found in the object, then it is silently added to the object and filled with a `null` value to make `key` a valid reference. In case the value was `null` before, it is converted to an object. @param[in] key key of the element to access @return reference to the element at key @a key @throw type_error.305 if the JSON value is not an object or null; in that cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and written using the `[]` operator.,operatorarray__key_type} @sa see @ref at(const typename object_t::key_type&) for access by reference with range checking @sa see @ref value() for access by value with a default value @since version 1.0.0 */ reference operator[](const typename object_t::key_type& key) { // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; m_value.object = create(); assert_invariant(); } // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { return set_parent(m_value.object->operator[](key)); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } /*! @brief read-only access specified object element Returns a const reference to the element at with specified key @a key. No bounds checking is performed. @warning If the element with key @a key does not exist, the behavior is undefined. @param[in] key key of the element to access @return const reference to the element at key @a key @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** @throw type_error.305 if the JSON value is not an object; in that case, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using the `[]` operator.,operatorarray__key_type_const} @sa see @ref at(const typename object_t::key_type&) for access by reference with range checking @sa see @ref value() for access by value with a default value @since version 1.0.0 */ const_reference operator[](const typename object_t::key_type& key) const { // const operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } /*! @brief access specified object element Returns a reference to the element at with specified key @a key. @note If @a key is not found in the object, then it is silently added to the object and filled with a `null` value to make `key` a valid reference. In case the value was `null` before, it is converted to an object. @param[in] key key of the element to access @return reference to the element at key @a key @throw type_error.305 if the JSON value is not an object or null; in that cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and written using the `[]` operator.,operatorarray__key_type} @sa see @ref at(const typename object_t::key_type&) for access by reference with range checking @sa see @ref value() for access by value with a default value @since version 1.1.0 */ template JSON_HEDLEY_NON_NULL(2) reference operator[](T* key) { // implicitly convert null to object if (is_null()) { m_type = value_t::object; m_value = value_t::object; assert_invariant(); } // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { return set_parent(m_value.object->operator[](key)); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } /*! @brief read-only access specified object element Returns a const reference to the element at with specified key @a key. No bounds checking is performed. @warning If the element with key @a key does not exist, the behavior is undefined. @param[in] key key of the element to access @return const reference to the element at key @a key @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** @throw type_error.305 if the JSON value is not an object; in that case, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using the `[]` operator.,operatorarray__key_type_const} @sa see @ref at(const typename object_t::key_type&) for access by reference with range checking @sa see @ref value() for access by value with a default value @since version 1.1.0 */ template JSON_HEDLEY_NON_NULL(2) const_reference operator[](T* key) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } /*! @brief access specified object element with default value Returns either a copy of an object's element at the specified key @a key or a given default value if no element with key @a key exists. The function is basically equivalent to executing @code {.cpp} try { return at(key); } catch(out_of_range) { return default_value; } @endcode @note Unlike @ref at(const typename object_t::key_type&), this function does not throw if the given key @a key was not found. @note Unlike @ref operator[](const typename object_t::key_type& key), this function does not implicitly add an element to the position defined by @a key. This function is furthermore also applicable to const objects. @param[in] key key of the element to access @param[in] default_value the value to return if @a key is not found @tparam ValueType type compatible to JSON values, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays. Note the type of the expected value at @a key and the default value @a default_value must be compatible. @return copy of the element at key @a key or @a default_value if @a key is not found @throw type_error.302 if @a default_value does not match the type of the value at @a key @throw type_error.306 if the JSON value is not an object; in that case, using `value()` with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be queried with a default value.,basic_json__value} @sa see @ref at(const typename object_t::key_type&) for access by reference with range checking @sa see @ref operator[](const typename object_t::key_type&) for unchecked access by reference @since version 1.0.0 */ // using std::is_convertible in a std::enable_if will fail when using explicit conversions template < class ValueType, typename std::enable_if < detail::is_getable::value && !std::is_same::value, int >::type = 0 > ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { // if key is found, return value and given default value otherwise const auto it = find(key); if (it != end()) { return it->template get(); } return default_value; } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); } /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const */ string_t value(const typename object_t::key_type& key, const char* default_value) const { return value(key, string_t(default_value)); } /*! @brief access specified object element via JSON Pointer with default value Returns either a copy of an object's element at the specified key @a key or a given default value if no element with key @a key exists. The function is basically equivalent to executing @code {.cpp} try { return at(ptr); } catch(out_of_range) { return default_value; } @endcode @note Unlike @ref at(const json_pointer&), this function does not throw if the given key @a key was not found. @param[in] ptr a JSON pointer to the element to access @param[in] default_value the value to return if @a ptr found no value @tparam ValueType type compatible to JSON values, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays. Note the type of the expected value at @a key and the default value @a default_value must be compatible. @return copy of the element at key @a key or @a default_value if @a key is not found @throw type_error.302 if @a default_value does not match the type of the value at @a ptr @throw type_error.306 if the JSON value is not an object; in that case, using `value()` with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be queried with a default value.,basic_json__value_ptr} @sa see @ref operator[](const json_pointer&) for unchecked access by reference @since version 2.0.2 */ template::value, int>::type = 0> ValueType value(const json_pointer& ptr, const ValueType& default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { // if pointer resolves a value, return it or use default value JSON_TRY { return ptr.get_checked(this).template get(); } JSON_INTERNAL_CATCH (out_of_range&) { return default_value; } } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); } /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const json_pointer&, ValueType) const */ JSON_HEDLEY_NON_NULL(3) string_t value(const json_pointer& ptr, const char* default_value) const { return value(ptr, string_t(default_value)); } /*! @brief access the first element Returns a reference to the first element in the container. For a JSON container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the first element is returned. In case of number, string, boolean, or binary values, a reference to the value is returned. @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) or an empty array or object (undefined behavior, **guarded by assertions**). @post The JSON value remains unchanged. @throw invalid_iterator.214 when called on `null` value @liveexample{The following code shows an example for `front()`.,front} @sa see @ref back() -- access the last element @since version 1.0.0 */ reference front() { return *begin(); } /*! @copydoc basic_json::front() */ const_reference front() const { return *cbegin(); } /*! @brief access the last element Returns a reference to the last element in the container. For a JSON container `c`, the expression `c.back()` is equivalent to @code {.cpp} auto tmp = c.end(); --tmp; return *tmp; @endcode @return In case of a structured type (array or object), a reference to the last element is returned. In case of number, string, boolean, or binary values, a reference to the value is returned. @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) or an empty array or object (undefined behavior, **guarded by assertions**). @post The JSON value remains unchanged. @throw invalid_iterator.214 when called on a `null` value. See example below. @liveexample{The following code shows an example for `back()`.,back} @sa see @ref front() -- access the first element @since version 1.0.0 */ reference back() { auto tmp = end(); --tmp; return *tmp; } /*! @copydoc basic_json::back() */ const_reference back() const { auto tmp = cend(); --tmp; return *tmp; } /*! @brief remove element given an iterator Removes the element specified by iterator @a pos. The iterator @a pos must be valid and dereferenceable. Thus the `end()` iterator (which is valid, but is not dereferenceable) cannot be used as a value for @a pos. If called on a primitive type other than `null`, the resulting JSON value will be `null`. @param[in] pos iterator to the element to remove @return Iterator following the last removed element. If the iterator @a pos refers to the last element, the `end()` iterator is returned. @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @throw type_error.307 if called on a `null` value; example: `"cannot use erase() with null"` @throw invalid_iterator.202 if called on an iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"` @throw invalid_iterator.205 if called on a primitive type with invalid iterator (i.e., any iterator which is not `begin()`); example: `"iterator out of range"` @complexity The complexity depends on the type: - objects: amortized constant - arrays: linear in distance between @a pos and the end of the container - strings and binary: linear in the length of the member - other types: constant @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa see @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa see @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ template < class IteratorType, typename std::enable_if < std::is_same::value || std::is_same::value, int >::type = 0 > IteratorType erase(IteratorType pos) { // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } IteratorType result = end(); switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::number_unsigned: case value_t::string: case value_t::binary: { if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) { JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); } if (is_string()) { AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.string); std::allocator_traits::deallocate(alloc, m_value.string, 1); m_value.string = nullptr; } else if (is_binary()) { AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; } m_type = value_t::null; assert_invariant(); break; } case value_t::object: { result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); break; } case value_t::array: { result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); break; } case value_t::null: case value_t::discarded: default: JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } return result; } /*! @brief remove elements given an iterator range Removes the element specified by the range `[first; last)`. The iterator @a first does not need to be dereferenceable if `first == last`: erasing an empty range is a no-op. If called on a primitive type other than `null`, the resulting JSON value will be `null`. @param[in] first iterator to the beginning of the range to remove @param[in] last iterator past the end of the range to remove @return Iterator following the last removed element. If the iterator @a second refers to the last element, the `end()` iterator is returned. @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @throw type_error.307 if called on a `null` value; example: `"cannot use erase() with null"` @throw invalid_iterator.203 if called on iterators which does not belong to the current JSON value; example: `"iterators do not fit current value"` @throw invalid_iterator.204 if called on a primitive type with invalid iterators (i.e., if `first != begin()` and `last != end()`); example: `"iterators out of range"` @complexity The complexity depends on the type: - objects: `log(size()) + std::distance(first, last)` - arrays: linear in the distance between @a first and @a last, plus linear in the distance between @a last and end of the container - strings and binary: linear in the length of the member - other types: constant @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} @sa see @ref erase(IteratorType) -- removes the element at a given position @sa see @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa see @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ template < class IteratorType, typename std::enable_if < std::is_same::value || std::is_same::value, int >::type = 0 > IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); } IteratorType result = end(); switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::number_unsigned: case value_t::string: case value_t::binary: { if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); } if (is_string()) { AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.string); std::allocator_traits::deallocate(alloc, m_value.string, 1); m_value.string = nullptr; } else if (is_binary()) { AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; } m_type = value_t::null; assert_invariant(); break; } case value_t::object: { result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); break; } case value_t::array: { result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); break; } case value_t::null: case value_t::discarded: default: JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } return result; } /*! @brief remove element from a JSON object given a key Removes elements from a JSON object with the key value @a key. @param[in] key value of the elements to remove @return Number of elements removed. If @a ObjectType is the default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). @post References and iterators to the erased elements are invalidated. Other references and iterators are not affected. @throw type_error.307 when called on a type other than JSON object; example: `"cannot use erase() with null"` @complexity `log(size()) + count(key)` @liveexample{The example shows the effect of `erase()`.,erase__key_type} @sa see @ref erase(IteratorType) -- removes the element at a given position @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa see @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ size_type erase(const typename object_t::key_type& key) { // this erase only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { return m_value.object->erase(key); } JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } /*! @brief remove element from a JSON array given an index Removes element from a JSON array at the index @a idx. @param[in] idx index of the element to remove @throw type_error.307 when called on a type other than JSON object; example: `"cannot use erase() with null"` @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 is out of range"` @complexity Linear in distance between @a idx and the end of the container. @liveexample{The example shows the effect of `erase()`.,erase__size_type} @sa see @ref erase(IteratorType) -- removes the element at a given position @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa see @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @since version 1.0.0 */ void erase(const size_type idx) { // this erase only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { if (JSON_HEDLEY_UNLIKELY(idx >= size())) { JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } } /// @} //////////// // lookup // //////////// /// @name lookup /// @{ /*! @brief find an element in a JSON object Finds an element in a JSON object with key equivalent to @a key. If the element is not found or the JSON value is not an object, end() is returned. @note This method always returns @ref end() when executed on a JSON type that is not an object. @param[in] key key value of the element to search for. @return Iterator to an element with key equivalent to @a key. If no such element is found or the JSON value is not an object, past-the-end (see @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @liveexample{The example shows how `find()` is used.,find__key_type} @sa see @ref contains(KeyT&&) const -- checks whether a key exists @since version 1.0.0 */ template iterator find(KeyT&& key) { auto result = end(); if (is_object()) { result.m_it.object_iterator = m_value.object->find(std::forward(key)); } return result; } /*! @brief find an element in a JSON object @copydoc find(KeyT&&) */ template const_iterator find(KeyT&& key) const { auto result = cend(); if (is_object()) { result.m_it.object_iterator = m_value.object->find(std::forward(key)); } return result; } /*! @brief returns the number of occurrences of a key in a JSON object Returns the number of elements with key @a key. If ObjectType is the default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). @note This method always returns `0` when executed on a JSON type that is not an object. @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an object, the return value will be `0`. @complexity Logarithmic in the size of the JSON object. @liveexample{The example shows how `count()` is used.,count} @since version 1.0.0 */ template size_type count(KeyT&& key) const { // return 0 for all nonobject types return is_object() ? m_value.object->count(std::forward(key)) : 0; } /*! @brief check the existence of an element in a JSON object Check whether an element exists in a JSON object with key equivalent to @a key. If the element is not found or the JSON value is not an object, false is returned. @note This method always returns false when executed on a JSON type that is not an object. @param[in] key key value to check its existence. @return true if an element with specified @a key exists. If no such element with such key is found or the JSON value is not an object, false is returned. @complexity Logarithmic in the size of the JSON object. @liveexample{The following code shows an example for `contains()`.,contains} @sa see @ref find(KeyT&&) -- returns an iterator to an object element @sa see @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer @since version 3.6.0 */ template < typename KeyT, typename std::enable_if < !std::is_same::type, json_pointer>::value, int >::type = 0 > bool contains(KeyT && key) const { return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); } /*! @brief check the existence of an element in a JSON object given a JSON pointer Check whether the given JSON pointer @a ptr can be resolved in the current JSON value. @note This method can be executed on any JSON value type. @param[in] ptr JSON pointer to check its existence. @return true if the JSON pointer can be resolved to a stored value, false otherwise. @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`. @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @complexity Logarithmic in the size of the JSON object. @liveexample{The following code shows an example for `contains()`.,contains_json_pointer} @sa see @ref contains(KeyT &&) const -- checks the existence of a key @since version 3.7.0 */ bool contains(const json_pointer& ptr) const { return ptr.contains(this); } /// @} /////////////// // iterators // /////////////// /// @name iterators /// @{ /*! @brief returns an iterator to the first element Returns an iterator to the first element. @image html range-begin-end.svg "Illustration from cppreference.com" @return iterator to the first element @complexity Constant. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is constant. @liveexample{The following code shows an example for `begin()`.,begin} @sa see @ref cbegin() -- returns a const iterator to the beginning @sa see @ref end() -- returns an iterator to the end @sa see @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ iterator begin() noexcept { iterator result(this); result.set_begin(); return result; } /*! @copydoc basic_json::cbegin() */ const_iterator begin() const noexcept { return cbegin(); } /*! @brief returns a const iterator to the first element Returns a const iterator to the first element. @image html range-begin-end.svg "Illustration from cppreference.com" @return const iterator to the first element @complexity Constant. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).begin()`. @liveexample{The following code shows an example for `cbegin()`.,cbegin} @sa see @ref begin() -- returns an iterator to the beginning @sa see @ref end() -- returns an iterator to the end @sa see @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ const_iterator cbegin() const noexcept { const_iterator result(this); result.set_begin(); return result; } /*! @brief returns an iterator to one past the last element Returns an iterator to one past the last element. @image html range-begin-end.svg "Illustration from cppreference.com" @return iterator one past the last element @complexity Constant. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is constant. @liveexample{The following code shows an example for `end()`.,end} @sa see @ref cend() -- returns a const iterator to the end @sa see @ref begin() -- returns an iterator to the beginning @sa see @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ iterator end() noexcept { iterator result(this); result.set_end(); return result; } /*! @copydoc basic_json::cend() */ const_iterator end() const noexcept { return cend(); } /*! @brief returns a const iterator to one past the last element Returns a const iterator to one past the last element. @image html range-begin-end.svg "Illustration from cppreference.com" @return const iterator one past the last element @complexity Constant. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).end()`. @liveexample{The following code shows an example for `cend()`.,cend} @sa see @ref end() -- returns an iterator to the end @sa see @ref begin() -- returns an iterator to the beginning @sa see @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ const_iterator cend() const noexcept { const_iterator result(this); result.set_end(); return result; } /*! @brief returns an iterator to the reverse-beginning Returns an iterator to the reverse-beginning; that is, the last element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function helps `basic_json` satisfying the [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(end())`. @liveexample{The following code shows an example for `rbegin()`.,rbegin} @sa see @ref crbegin() -- returns a const reverse iterator to the beginning @sa see @ref rend() -- returns a reverse iterator to the end @sa see @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } /*! @copydoc basic_json::crbegin() */ const_reverse_iterator rbegin() const noexcept { return crbegin(); } /*! @brief returns an iterator to the reverse-end Returns an iterator to the reverse-end; that is, one before the first element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function helps `basic_json` satisfying the [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(begin())`. @liveexample{The following code shows an example for `rend()`.,rend} @sa see @ref crend() -- returns a const reverse iterator to the end @sa see @ref rbegin() -- returns a reverse iterator to the beginning @sa see @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ reverse_iterator rend() noexcept { return reverse_iterator(begin()); } /*! @copydoc basic_json::crend() */ const_reverse_iterator rend() const noexcept { return crend(); } /*! @brief returns a const reverse iterator to the last element Returns a const iterator to the reverse-beginning; that is, the last element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function helps `basic_json` satisfying the [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).rbegin()`. @liveexample{The following code shows an example for `crbegin()`.,crbegin} @sa see @ref rbegin() -- returns a reverse iterator to the beginning @sa see @ref rend() -- returns a reverse iterator to the end @sa see @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } /*! @brief returns a const reverse iterator to one before the first Returns a const reverse iterator to the reverse-end; that is, one before the first element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function helps `basic_json` satisfying the [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).rend()`. @liveexample{The following code shows an example for `crend()`.,crend} @sa see @ref rend() -- returns a reverse iterator to the end @sa see @ref rbegin() -- returns a reverse iterator to the beginning @sa see @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } public: /*! @brief wrapper to access iterator member functions in range-based for This function allows to access @ref iterator::key() and @ref iterator::value() during range-based for loops. In these loops, a reference to the JSON values is returned, so there is no access to the underlying iterator. For loop without iterator_wrapper: @code{cpp} for (auto it = j_object.begin(); it != j_object.end(); ++it) { std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; } @endcode Range-based for loop without iterator proxy: @code{cpp} for (auto it : j_object) { // "it" is of type json::reference and has no key() member std::cout << "value: " << it << '\n'; } @endcode Range-based for loop with iterator proxy: @code{cpp} for (auto it : json::iterator_wrapper(j_object)) { std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; } @endcode @note When iterating over an array, `key()` will return the index of the element as string (see example). @param[in] ref reference to a JSON value @return iteration proxy object wrapping @a ref with an interface to use in range-based for loops @liveexample{The following code shows how the wrapper is used,iterator_wrapper} @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @note The name of this function is not yet final and may change in the future. @deprecated This stream operator is deprecated and will be removed in future 4.0.0 of the library. Please use @ref items() instead; that is, replace `json::iterator_wrapper(j)` with `j.items()`. */ JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) static iteration_proxy iterator_wrapper(reference ref) noexcept { return ref.items(); } /*! @copydoc iterator_wrapper(reference) */ JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) static iteration_proxy iterator_wrapper(const_reference ref) noexcept { return ref.items(); } /*! @brief helper to access iterator member functions in range-based for This function allows to access @ref iterator::key() and @ref iterator::value() during range-based for loops. In these loops, a reference to the JSON values is returned, so there is no access to the underlying iterator. For loop without `items()` function: @code{cpp} for (auto it = j_object.begin(); it != j_object.end(); ++it) { std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; } @endcode Range-based for loop without `items()` function: @code{cpp} for (auto it : j_object) { // "it" is of type json::reference and has no key() member std::cout << "value: " << it << '\n'; } @endcode Range-based for loop with `items()` function: @code{cpp} for (auto& el : j_object.items()) { std::cout << "key: " << el.key() << ", value:" << el.value() << '\n'; } @endcode The `items()` function also allows to use [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) (C++17): @code{cpp} for (auto& [key, val] : j_object.items()) { std::cout << "key: " << key << ", value:" << val << '\n'; } @endcode @note When iterating over an array, `key()` will return the index of the element as string (see example). For primitive types (e.g., numbers), `key()` returns an empty string. @warning Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exeeds the iteration. See for more information. @return iteration proxy object wrapping @a ref with an interface to use in range-based for loops @liveexample{The following code shows how the function is used.,items} @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 3.1.0, structured bindings support since 3.5.0. */ iteration_proxy items() noexcept { return iteration_proxy(*this); } /*! @copydoc items() */ iteration_proxy items() const noexcept { return iteration_proxy(*this); } /// @} ////////////// // capacity // ////////////// /// @name capacity /// @{ /*! @brief checks whether the container is empty. Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | `true` boolean | `false` string | `false` number | `false` binary | `false` object | result of function `object_t::empty()` array | result of function `array_t::empty()` @liveexample{The following code uses `empty()` to check if a JSON object contains any elements.,empty} @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their `empty()` functions have constant complexity. @iterators No changes. @exceptionsafety No-throw guarantee: this function never throws exceptions. @note This function does not return whether a string stored as JSON value is empty - it returns whether the JSON container itself is empty which is false in the case of a string. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is constant. - Has the semantics of `begin() == end()`. @sa see @ref size() -- returns the number of elements @since version 1.0.0 */ bool empty() const noexcept { switch (m_type) { case value_t::null: { // null values are empty return true; } case value_t::array: { // delegate call to array_t::empty() return m_value.array->empty(); } case value_t::object: { // delegate call to object_t::empty() return m_value.object->empty(); } case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { // all other types are nonempty return false; } } } /*! @brief returns the number of elements Returns the number of elements in a JSON value. @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | `0` boolean | `1` string | `1` number | `1` binary | `1` object | result of function object_t::size() array | result of function array_t::size() @liveexample{The following code calls `size()` on the different value types.,size} @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their size() functions have constant complexity. @iterators No changes. @exceptionsafety No-throw guarantee: this function never throws exceptions. @note This function does not return the length of a string stored as JSON value - it returns the number of elements in the JSON value which is 1 in the case of a string. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is constant. - Has the semantics of `std::distance(begin(), end())`. @sa see @ref empty() -- checks whether the container is empty @sa see @ref max_size() -- returns the maximal number of elements @since version 1.0.0 */ size_type size() const noexcept { switch (m_type) { case value_t::null: { // null values are empty return 0; } case value_t::array: { // delegate call to array_t::size() return m_value.array->size(); } case value_t::object: { // delegate call to object_t::size() return m_value.object->size(); } case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { // all other types have size 1 return 1; } } } /*! @brief returns the maximum possible number of elements Returns the maximum number of elements a JSON value is able to hold due to system or library implementation limitations, i.e. `std::distance(begin(), end())` for the JSON value. @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | `0` (same as `size()`) boolean | `1` (same as `size()`) string | `1` (same as `size()`) number | `1` (same as `size()`) binary | `1` (same as `size()`) object | result of function `object_t::max_size()` array | result of function `array_t::max_size()` @liveexample{The following code calls `max_size()` on the different value types. Note the output is implementation specific.,max_size} @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their `max_size()` functions have constant complexity. @iterators No changes. @exceptionsafety No-throw guarantee: this function never throws exceptions. @requirement This function helps `basic_json` satisfying the [Container](https://en.cppreference.com/w/cpp/named_req/Container) requirements: - The complexity is constant. - Has the semantics of returning `b.size()` where `b` is the largest possible JSON value. @sa see @ref size() -- returns the number of elements @since version 1.0.0 */ size_type max_size() const noexcept { switch (m_type) { case value_t::array: { // delegate call to array_t::max_size() return m_value.array->max_size(); } case value_t::object: { // delegate call to object_t::max_size() return m_value.object->max_size(); } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { // all other types have max_size() == size() return size(); } } } /// @} /////////////// // modifiers // /////////////// /// @name modifiers /// @{ /*! @brief clears the contents Clears the content of a JSON value and resets it to the default value as if @ref basic_json(value_t) would have been called with the current value type from @ref type(): Value type | initial value ----------- | ------------- null | `null` boolean | `false` string | `""` number | `0` binary | An empty byte vector object | `{}` array | `[]` @post Has the same effect as calling @code {.cpp} *this = basic_json(type()); @endcode @liveexample{The example below shows the effect of `clear()` to different JSON types.,clear} @complexity Linear in the size of the JSON value. @iterators All iterators, pointers and references related to this container are invalidated. @exceptionsafety No-throw guarantee: this function never throws exceptions. @sa see @ref basic_json(value_t) -- constructor that creates an object with the same value than calling `clear()` @since version 1.0.0 */ void clear() noexcept { switch (m_type) { case value_t::number_integer: { m_value.number_integer = 0; break; } case value_t::number_unsigned: { m_value.number_unsigned = 0; break; } case value_t::number_float: { m_value.number_float = 0.0; break; } case value_t::boolean: { m_value.boolean = false; break; } case value_t::string: { m_value.string->clear(); break; } case value_t::binary: { m_value.binary->clear(); break; } case value_t::array: { m_value.array->clear(); break; } case value_t::object: { m_value.object->clear(); break; } case value_t::null: case value_t::discarded: default: break; } } /*! @brief add an object to an array Appends the given element @a val to the end of the JSON value. If the function is called on a JSON null value, an empty array is created before appending @a val. @param[in] val the value to add to the JSON array @throw type_error.308 when called on a type other than JSON array or null; example: `"cannot use push_back() with number"` @complexity Amortized constant. @liveexample{The example shows how `push_back()` and `+=` can be used to add elements to a JSON array. Note how the `null` value was silently converted to a JSON array.,push_back} @since version 1.0.0 */ void push_back(basic_json&& val) { // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an array if (is_null()) { m_type = value_t::array; m_value = value_t::array; assert_invariant(); } // add element to array (move semantics) const auto old_capacity = m_value.array->capacity(); m_value.array->push_back(std::move(val)); set_parent(m_value.array->back(), old_capacity); // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ reference operator+=(basic_json&& val) { push_back(std::move(val)); return *this; } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ void push_back(const basic_json& val) { // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an array if (is_null()) { m_type = value_t::array; m_value = value_t::array; assert_invariant(); } // add element to array const auto old_capacity = m_value.array->capacity(); m_value.array->push_back(val); set_parent(m_value.array->back(), old_capacity); } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ reference operator+=(const basic_json& val) { push_back(val); return *this; } /*! @brief add an object to an object Inserts the given element @a val to the JSON object. If the function is called on a JSON null value, an empty object is created before inserting @a val. @param[in] val the value to add to the JSON object @throw type_error.308 when called on a type other than JSON object or null; example: `"cannot use push_back() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @liveexample{The example shows how `push_back()` and `+=` can be used to add elements to a JSON object. Note how the `null` value was silently converted to a JSON object.,push_back__object_t__value} @since version 1.0.0 */ void push_back(const typename object_t::value_type& val) { // push_back only works for null objects or objects if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an object if (is_null()) { m_type = value_t::object; m_value = value_t::object; assert_invariant(); } // add element to object auto res = m_value.object->insert(val); set_parent(res.first->second); } /*! @brief add an object to an object @copydoc push_back(const typename object_t::value_type&) */ reference operator+=(const typename object_t::value_type& val) { push_back(val); return *this; } /*! @brief add an object to an object This function allows to use `push_back` with an initializer list. In case 1. the current value is an object, 2. the initializer list @a d2Init contains only two elements, and 3. the first element of @a d2Init is a string, @a d2Init is converted into an object element and added using @ref push_back(const typename object_t::value_type&). Otherwise, @a d2Init is converted to a JSON value and added using @ref push_back(basic_json&&). @param[in] init an initializer list @complexity Linear in the size of the initializer list @a d2Init. @note This function is required to resolve an ambiguous overload error, because pairs like `{"key", "value"}` can be both interpreted as `object_t::value_type` or `std::initializer_list`, see https://github.com/nlohmann/json/issues/235 for more information. @liveexample{The example shows how initializer lists are treated as objects when possible.,push_back__initializer_list} */ void push_back(initializer_list_t init) { if (is_object() && init.size() == 2 && (*init.begin())->is_string()) { basic_json&& key = init.begin()->moved_or_copied(); push_back(typename object_t::value_type( std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); } else { push_back(basic_json(init)); } } /*! @brief add an object to an object @copydoc push_back(initializer_list_t) */ reference operator+=(initializer_list_t init) { push_back(init); return *this; } /*! @brief add an object to an array Creates a JSON value from the passed parameters @a args to the end of the JSON value. If the function is called on a JSON null value, an empty array is created before appending the value created from @a args. @param[in] args arguments to forward to a constructor of @ref basic_json @tparam Args compatible types to create a @ref basic_json object @return reference to the inserted element @throw type_error.311 when called on a type other than JSON array or null; example: `"cannot use emplace_back() with number"` @complexity Amortized constant. @liveexample{The example shows how `push_back()` can be used to add elements to a JSON array. Note how the `null` value was silently converted to a JSON array.,emplace_back} @since version 2.0.8, returns reference since 3.7.0 */ template reference emplace_back(Args&& ... args) { // emplace_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); } // transform null object into an array if (is_null()) { m_type = value_t::array; m_value = value_t::array; assert_invariant(); } // add element to array (perfect forwarding) const auto old_capacity = m_value.array->capacity(); m_value.array->emplace_back(std::forward(args)...); return set_parent(m_value.array->back(), old_capacity); } /*! @brief add an object to an object if key does not exist Inserts a new element into a JSON object constructed in-place with the given @a args if there is no element with the key in the container. If the function is called on a JSON null value, an empty object is created before appending the value created from @a args. @param[in] args arguments to forward to a constructor of @ref basic_json @tparam Args compatible types to create a @ref basic_json object @return a pair consisting of an iterator to the inserted element, or the already-existing element if no insertion happened, and a bool denoting whether the insertion took place. @throw type_error.311 when called on a type other than JSON object or null; example: `"cannot use emplace() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @liveexample{The example shows how `emplace()` can be used to add elements to a JSON object. Note how the `null` value was silently converted to a JSON object. Further note how no value is added if there was already one value stored with the same key.,emplace} @since version 2.0.8 */ template std::pair emplace(Args&& ... args) { // emplace only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); } // transform null object into an object if (is_null()) { m_type = value_t::object; m_value = value_t::object; assert_invariant(); } // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); set_parent(res.first->second); // create result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; // return pair of iterator and boolean return {it, res.second}; } /// Helper for insertion of an iterator /// @note: This uses std::distance to support GCC 4.8, /// see https://github.com/nlohmann/json/pull/1257 template iterator insert_iterator(const_iterator pos, Args&& ... args) { iterator result(this); JSON_ASSERT(m_value.array != nullptr); auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); result.m_it.array_iterator = m_value.array->begin() + insert_pos; // This could have been written as: // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); // but the return value of insert is missing in GCC 4.8, so it is written this way instead. set_parents(); return result; } /*! @brief inserts element Inserts element @a val before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] val element to insert @return iterator pointing to the inserted @a val. @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @complexity Constant plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how `insert()` is used.,insert} @since version 1.0.0 */ iterator insert(const_iterator pos, const basic_json& val) { // insert only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator return insert_iterator(pos, val); } JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } /*! @brief inserts element @copydoc insert(const_iterator, const basic_json&) */ iterator insert(const_iterator pos, basic_json&& val) { return insert(pos, val); } /*! @brief inserts elements Inserts @a cnt copies of @a val before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] cnt number of copies of @a val to insert @param[in] val element to insert @return iterator pointing to the first element inserted, or @a pos if `cnt==0` @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @complexity Linear in @a cnt plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how `insert()` is used.,insert__count} @since version 1.0.0 */ iterator insert(const_iterator pos, size_type cnt, const basic_json& val) { // insert only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator return insert_iterator(pos, cnt, val); } JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } /*! @brief inserts elements Inserts elements from range `[first, last)` before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @throw invalid_iterator.210 if @a first and @a last do not belong to the same JSON value; example: `"iterators do not fit"` @throw invalid_iterator.211 if @a first or @a last are iterators into container for which insert is called; example: `"passed iterators may not belong to container"` @return iterator pointing to the first element inserted, or @a pos if `first==last` @complexity Linear in `std::distance(first, last)` plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how `insert()` is used.,insert__range} @since version 1.0.0 */ iterator insert(const_iterator pos, const_iterator first, const_iterator last) { // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); } // insert to array and return iterator return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); } /*! @brief inserts elements Inserts elements from initializer list @a ilist before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] ilist initializer list to insert the values from @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @return iterator pointing to the first element inserted, or @a pos if `ilist` is empty @complexity Linear in `ilist.size()` plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how `insert()` is used.,insert__ilist} @since version 1.0.0 */ iterator insert(const_iterator pos, initializer_list_t ilist) { // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator return insert_iterator(pos, ilist.begin(), ilist.end()); } /*! @brief inserts elements Inserts elements from range `[first, last)`. @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert @throw type_error.309 if called on JSON values other than objects; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if iterator @a first or @a last does does not point to an object; example: `"iterators first and last must point to objects"` @throw invalid_iterator.210 if @a first and @a last do not belong to the same JSON value; example: `"iterators do not fit"` @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number of elements to insert. @liveexample{The example shows how `insert()` is used.,insert__range_object} @since version 3.0.0 */ void insert(const_iterator first, const_iterator last) { // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); } /*! @brief updates a JSON object from another object, overwriting existing keys Inserts all values from JSON object @a j and overwrites existing keys. @param[in] j JSON object to read values from @throw type_error.312 if called on JSON values other than objects; example: `"cannot use update() with string"` @complexity O(N*log(size() + N)), where N is the number of elements to insert. @liveexample{The example shows how `update()` is used.,update} @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update @since version 3.0.0 */ void update(const_reference j) { // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; m_value.object = create(); assert_invariant(); } if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); } if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), *this)); } for (auto it = j.cbegin(); it != j.cend(); ++it) { m_value.object->operator[](it.key()) = it.value(); #if JSON_DIAGNOSTICS m_value.object->operator[](it.key()).m_parent = this; #endif } } /*! @brief updates a JSON object from another object, overwriting existing keys Inserts all values from from range `[first, last)` and overwrites existing keys. @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert @throw type_error.312 if called on JSON values other than objects; example: `"cannot use update() with string"` @throw invalid_iterator.202 if iterator @a first or @a last does does not point to an object; example: `"iterators first and last must point to objects"` @throw invalid_iterator.210 if @a first and @a last do not belong to the same JSON value; example: `"iterators do not fit"` @complexity O(N*log(size() + N)), where N is the number of elements to insert. @liveexample{The example shows how `update()` is used__range.,update} @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update @since version 3.0.0 */ void update(const_iterator first, const_iterator last) { // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; m_value.object = create(); assert_invariant(); } if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() || !last.m_object->is_object())) { JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); } for (auto it = first; it != last; ++it) { m_value.object->operator[](it.key()) = it.value(); #if JSON_DIAGNOSTICS m_value.object->operator[](it.key()).m_parent = this; #endif } } /*! @brief exchanges the values Exchanges the contents of the JSON value with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other JSON value to exchange the contents with @complexity Constant. @liveexample{The example below shows how JSON values can be swapped with `swap()`.,swap__reference} @since version 1.0.0 */ void swap(reference other) noexcept ( std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value&& std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); set_parents(); other.set_parents(); assert_invariant(); } /*! @brief exchanges the values Exchanges the contents of the JSON value from @a left with those of @a right. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. implemented as a friend function callable via ADL. @param[in,out] left JSON value to exchange the contents with @param[in,out] right JSON value to exchange the contents with @complexity Constant. @liveexample{The example below shows how JSON values can be swapped with `swap()`.,swap__reference} @since version 1.0.0 */ friend void swap(reference left, reference right) noexcept ( std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value&& std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value ) { left.swap(right); } /*! @brief exchanges the values Exchanges the contents of a JSON array with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other array to exchange the contents with @throw type_error.310 when JSON value is not an array; example: `"cannot use swap() with string"` @complexity Constant. @liveexample{The example below shows how arrays can be swapped with `swap()`.,swap__array_t} @since version 1.0.0 */ void swap(array_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { std::swap(*(m_value.array), other); } else { JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } /*! @brief exchanges the values Exchanges the contents of a JSON object with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other object to exchange the contents with @throw type_error.310 when JSON value is not an object; example: `"cannot use swap() with string"` @complexity Constant. @liveexample{The example below shows how objects can be swapped with `swap()`.,swap__object_t} @since version 1.0.0 */ void swap(object_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { std::swap(*(m_value.object), other); } else { JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } /*! @brief exchanges the values Exchanges the contents of a JSON string with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other string to exchange the contents with @throw type_error.310 when JSON value is not a string; example: `"cannot use swap() with boolean"` @complexity Constant. @liveexample{The example below shows how strings can be swapped with `swap()`.,swap__string_t} @since version 1.0.0 */ void swap(string_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for strings if (JSON_HEDLEY_LIKELY(is_string())) { std::swap(*(m_value.string), other); } else { JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } /*! @brief exchanges the values Exchanges the contents of a JSON string with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other binary to exchange the contents with @throw type_error.310 when JSON value is not a string; example: `"cannot use swap() with boolean"` @complexity Constant. @liveexample{The example below shows how strings can be swapped with `swap()`.,swap__binary_t} @since version 3.8.0 */ void swap(binary_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for strings if (JSON_HEDLEY_LIKELY(is_binary())) { std::swap(*(m_value.binary), other); } else { JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } /// @copydoc swap(binary_t&) void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) { // swap only works for strings if (JSON_HEDLEY_LIKELY(is_binary())) { std::swap(*(m_value.binary), other); } else { JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } /// @} public: ////////////////////////////////////////// // lexicographical comparison operators // ////////////////////////////////////////// /// @name lexicographical comparison operators /// @{ /*! @brief comparison: equal Compares two JSON values for equality according to the following rules: - Two JSON values are equal if (1) they are from the same type and (2) their stored values are the same according to their respective `operator==`. - Integer and floating-point numbers are automatically converted before comparison. Note that two NaN values are always treated as unequal. - Two JSON null values are equal. @note Floating-point inside JSON values numbers are compared with `json::number_float_t::operator==` which is `double::operator==` by default. To compare floating-point while respecting an epsilon, an alternative [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39) could be used, for instance @code {.cpp} template::value, T>::type> inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept { return std::abs(a - b) <= epsilon; } @endcode Or you can self-defined operator equal function like this: @code {.cpp} bool my_equal(const_reference lhs, const_reference rhs) { const auto lhs_type lhs.type(); const auto rhs_type rhs.type(); if (lhs_type == rhs_type) { switch(lhs_type) // self_defined case case value_t::number_float: return std::abs(lhs - rhs) <= std::numeric_limits::epsilon(); // other cases remain the same with the original ... } ... } @endcode @note NaN values never compare equal to themselves or to other NaN values. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether the values @a lhs and @a rhs are equal @exceptionsafety No-throw guarantee: this function never throws exceptions. @complexity Linear. @liveexample{The example demonstrates comparing several JSON types.,operator__equal} @since version 1.0.0 */ friend bool operator==(const_reference lhs, const_reference rhs) noexcept { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif const auto lhs_type = lhs.type(); const auto rhs_type = rhs.type(); if (lhs_type == rhs_type) { switch (lhs_type) { case value_t::array: return *lhs.m_value.array == *rhs.m_value.array; case value_t::object: return *lhs.m_value.object == *rhs.m_value.object; case value_t::null: return true; case value_t::string: return *lhs.m_value.string == *rhs.m_value.string; case value_t::boolean: return lhs.m_value.boolean == rhs.m_value.boolean; case value_t::number_integer: return lhs.m_value.number_integer == rhs.m_value.number_integer; case value_t::number_unsigned: return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; case value_t::number_float: return lhs.m_value.number_float == rhs.m_value.number_float; case value_t::binary: return *lhs.m_value.binary == *rhs.m_value.binary; case value_t::discarded: default: return false; } } else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) { return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; } else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) { return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); } else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) { return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; } else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); } else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) { return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; } else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); } return false; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif } /*! @brief comparison: equal @copydoc operator==(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator==(const_reference lhs, ScalarType rhs) noexcept { return lhs == basic_json(rhs); } /*! @brief comparison: equal @copydoc operator==(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator==(ScalarType lhs, const_reference rhs) noexcept { return basic_json(lhs) == rhs; } /*! @brief comparison: not equal Compares two JSON values for inequality by calculating `not (lhs == rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether the values @a lhs and @a rhs are not equal @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__notequal} @since version 1.0.0 */ friend bool operator!=(const_reference lhs, const_reference rhs) noexcept { return !(lhs == rhs); } /*! @brief comparison: not equal @copydoc operator!=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept { return lhs != basic_json(rhs); } /*! @brief comparison: not equal @copydoc operator!=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept { return basic_json(lhs) != rhs; } /*! @brief comparison: less than Compares whether one JSON value @a lhs is less than another JSON value @a rhs according to the following rules: - If @a lhs and @a rhs have the same type, the values are compared using the default `<` operator. - Integer and floating-point numbers are automatically converted before comparison - In case @a lhs and @a rhs have different types, the values are ignored and the order of the types is considered, see @ref operator<(const value_t, const value_t). @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is less than @a rhs @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__less} @since version 1.0.0 */ friend bool operator<(const_reference lhs, const_reference rhs) noexcept { const auto lhs_type = lhs.type(); const auto rhs_type = rhs.type(); if (lhs_type == rhs_type) { switch (lhs_type) { case value_t::array: // note parentheses are necessary, see // https://github.com/nlohmann/json/issues/1530 return (*lhs.m_value.array) < (*rhs.m_value.array); case value_t::object: return (*lhs.m_value.object) < (*rhs.m_value.object); case value_t::null: return false; case value_t::string: return (*lhs.m_value.string) < (*rhs.m_value.string); case value_t::boolean: return (lhs.m_value.boolean) < (rhs.m_value.boolean); case value_t::number_integer: return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); case value_t::number_unsigned: return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); case value_t::number_float: return (lhs.m_value.number_float) < (rhs.m_value.number_float); case value_t::binary: return (*lhs.m_value.binary) < (*rhs.m_value.binary); case value_t::discarded: default: return false; } } else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) { return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; } else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) { return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); } else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) { return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; } else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); } else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); } else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) { return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; } // We only reach this line if we cannot compare values. In that case, // we compare types. Note we have to call the operator explicitly, // because MSVC has problems otherwise. return operator<(lhs_type, rhs_type); } /*! @brief comparison: less than @copydoc operator<(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator<(const_reference lhs, ScalarType rhs) noexcept { return lhs < basic_json(rhs); } /*! @brief comparison: less than @copydoc operator<(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator<(ScalarType lhs, const_reference rhs) noexcept { return basic_json(lhs) < rhs; } /*! @brief comparison: less than or equal Compares whether one JSON value @a lhs is less than or equal to another JSON value by calculating `not (rhs < lhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is less than or equal to @a rhs @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__greater} @since version 1.0.0 */ friend bool operator<=(const_reference lhs, const_reference rhs) noexcept { return !(rhs < lhs); } /*! @brief comparison: less than or equal @copydoc operator<=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept { return lhs <= basic_json(rhs); } /*! @brief comparison: less than or equal @copydoc operator<=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept { return basic_json(lhs) <= rhs; } /*! @brief comparison: greater than Compares whether one JSON value @a lhs is greater than another JSON value by calculating `not (lhs <= rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is greater than to @a rhs @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__lessequal} @since version 1.0.0 */ friend bool operator>(const_reference lhs, const_reference rhs) noexcept { return !(lhs <= rhs); } /*! @brief comparison: greater than @copydoc operator>(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator>(const_reference lhs, ScalarType rhs) noexcept { return lhs > basic_json(rhs); } /*! @brief comparison: greater than @copydoc operator>(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator>(ScalarType lhs, const_reference rhs) noexcept { return basic_json(lhs) > rhs; } /*! @brief comparison: greater than or equal Compares whether one JSON value @a lhs is greater than or equal to another JSON value by calculating `not (lhs < rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is greater than or equal to @a rhs @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__greaterequal} @since version 1.0.0 */ friend bool operator>=(const_reference lhs, const_reference rhs) noexcept { return !(lhs < rhs); } /*! @brief comparison: greater than or equal @copydoc operator>=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept { return lhs >= basic_json(rhs); } /*! @brief comparison: greater than or equal @copydoc operator>=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept { return basic_json(lhs) >= rhs; } /// @} /////////////////// // serialization // /////////////////// /// @name serialization /// @{ #ifndef JSON_NO_IO /*! @brief serialize to stream Serialize the given JSON value @a j to the output stream @a o. The JSON value will be serialized using the @ref dump member function. - The indentation of the output can be controlled with the member variable `width` of the output stream @a o. For instance, using the manipulator `std::setw(4)` on @a o sets the indentation level to `4` and the serialization result is the same as calling `dump(4)`. - The indentation character can be controlled with the member variable `fill` of the output stream @a o. For instance, the manipulator `std::setfill('\\t')` sets indentation to use a tab character rather than the default space character. @param[in,out] o stream to serialize to @param[in] j JSON value to serialize @return the stream @a o @throw type_error.316 if a string stored inside the JSON value is not UTF-8 encoded @complexity Linear. @liveexample{The example below shows the serialization with different parameters to `width` to adjust the indentation level.,operator_serialize} @since version 1.0.0; indentation character added in version 3.0.0 */ friend std::ostream& operator<<(std::ostream& o, const basic_json& j) { // read width member and use it as indentation parameter if nonzero const bool pretty_print = o.width() > 0; const auto indentation = pretty_print ? o.width() : 0; // reset width to 0 for subsequent calls to this stream o.width(0); // do the actual serialization serializer s(detail::output_adapter(o), o.fill()); s.dump(j, pretty_print, false, static_cast(indentation)); return o; } /*! @brief serialize to stream @deprecated This stream operator is deprecated and will be removed in future 4.0.0 of the library. Please use @ref operator<<(std::ostream&, const basic_json&) instead; that is, replace calls like `j >> o;` with `o << j;`. @since version 1.0.0; deprecated since version 3.0.0 */ JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { return o << j; } #endif // JSON_NO_IO /// @} ///////////////////// // deserialization // ///////////////////// /// @name deserialization /// @{ /*! @brief deserialize from a compatible input @tparam InputType A compatible input, for instance - an std::istream object - a FILE pointer - a C-style array of characters - a pointer to a null-terminated string of single byte characters - an object obj for which begin(obj) and end(obj) produces a valid pair of iterators. @param[in] i input to read from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) @param[in] ignore_comments whether comments should be ignored and treated like whitespace (true) or yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be value_t::discarded. @throw parse_error.101 if a parse error occurs; example: `""unexpected end of input; expected string literal""` @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb or reading from the input @a i has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below demonstrates the `parse()` function reading from an array.,parse__array__parser_callback_t} @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} @liveexample{The example below demonstrates the `parse()` function reading from a contiguous container.,parse__contiguouscontainer__parser_callback_t} @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to ignore comments. */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(InputType&& i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false) { basic_json result; parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } /*! @brief deserialize from a pair of character iterators The value_type of the iterator must be a integral type with size of 1, 2 or 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. @param[in] first iterator to start of character range @param[in] last iterator to end of character range @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) @param[in] ignore_comments whether comments should be ignored and treated like whitespace (true) or yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be value_t::discarded. @throw parse_error.101 if a parse error occurs; example: `""unexpected end of input; expected string literal""` @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false) { basic_json result; parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false) { basic_json result; parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } /*! @brief check if the input is valid JSON Unlike the @ref parse(InputType&&, const parser_callback_t,const bool) function, this function neither throws an exception in case of invalid JSON input (i.e., a parse error) nor creates diagnostic information. @tparam InputType A compatible input, for instance - an std::istream object - a FILE pointer - a C-style array of characters - a pointer to a null-terminated string of single byte characters - an object obj for which begin(obj) and end(obj) produces a valid pair of iterators. @param[in] i input to read from @param[in] ignore_comments whether comments should be ignored and treated like whitespace (true) or yield a parse error (true); (optional, false by default) @return Whether the input read from @a i is valid JSON. @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below demonstrates the `accept()` function reading from a string.,accept__string} */ template static bool accept(InputType&& i, const bool ignore_comments = false) { return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); } template static bool accept(IteratorType first, IteratorType last, const bool ignore_comments = false) { return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) static bool accept(detail::span_input_adapter&& i, const bool ignore_comments = false) { return parser(i.get(), nullptr, false, ignore_comments).accept(true); } /*! @brief generate SAX events The SAX event lister must follow the interface of @ref json_sax. This function reads from a compatible input. Examples are: - an std::istream object - a FILE pointer - a C-style array of characters - a pointer to a null-terminated string of single byte characters - an object obj for which begin(obj) and end(obj) produces a valid pair of iterators. @param[in] i input to read from @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely @param[in] ignore_comments whether comments should be ignored and treated like whitespace (true) or yield a parse error (true); (optional, false by default); only applies to the JSON file format. @return return value of the last processed SAX event @throw parse_error.101 if a parse error occurs; example: `""unexpected end of input; expected string literal""` @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the SAX consumer @a sax has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below demonstrates the `sax_parse()` function reading from string and processing the events with a user-defined SAX event consumer.,sax_parse} @since version 3.2.0 */ template JSON_HEDLEY_NON_NULL(2) static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, const bool strict = true, const bool ignore_comments = false) { auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } template JSON_HEDLEY_NON_NULL(3) static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, input_format_t format = input_format_t::json, const bool strict = true, const bool ignore_comments = false) { auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } template JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) JSON_HEDLEY_NON_NULL(2) static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, const bool strict = true, const bool ignore_comments = false) { auto ia = i.get(); return format == input_format_t::json // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } #ifndef JSON_NO_IO /*! @brief deserialize from stream @deprecated This stream operator is deprecated and will be removed in version 4.0.0 of the library. Please use @ref operator>>(std::istream&, basic_json&) instead; that is, replace calls like `j << i;` with `i >> j;`. @since version 1.0.0; deprecated since version 3.0.0 */ JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) friend std::istream& operator<<(basic_json& j, std::istream& i) { return operator>>(i, j); } /*! @brief deserialize from stream Deserializes an input stream to a JSON value. @param[in,out] i input stream to read a serialized JSON value from @param[in,out] j JSON value to write the deserialized input to @throw parse_error.101 in case of an unexpected token @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below shows how a JSON value is constructed by reading a serialization from a stream.,operator_deserialize} @sa parse(std::istream&, const parser_callback_t) for a variant with a parser callback function to filter values while parsing @since version 1.0.0 */ friend std::istream& operator>>(std::istream& i, basic_json& j) { parser(detail::input_adapter(i)).parse(false, j); return i; } #endif // JSON_NO_IO /// @} /////////////////////////// // convenience functions // /////////////////////////// /*! @brief return the type as string Returns the type name as string to be used in error messages - usually to indicate that a function was called on a wrong JSON type. @return a string representation of a the @a m_type member: Value type | return value ----------- | ------------- null | `"null"` boolean | `"boolean"` string | `"string"` number | `"number"` (for all number types) object | `"object"` array | `"array"` binary | `"binary"` discarded | `"discarded"` @exceptionsafety No-throw guarantee: this function never throws exceptions. @complexity Constant. @liveexample{The following code exemplifies `type_name()` for all JSON types.,type_name} @sa see @ref type() -- return the type of the JSON value @sa see @ref operator value_t() -- return the type of the JSON value (implicit) @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` since 3.0.0 */ JSON_HEDLEY_RETURNS_NON_NULL const char* type_name() const noexcept { { switch (m_type) { case value_t::null: return "null"; case value_t::object: return "object"; case value_t::array: return "array"; case value_t::string: return "string"; case value_t::boolean: return "boolean"; case value_t::binary: return "binary"; case value_t::discarded: return "discarded"; case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: default: return "number"; } } } JSON_PRIVATE_UNLESS_TESTED: ////////////////////// // member variables // ////////////////////// /// the type of the current element value_t m_type = value_t::null; /// the value of the current element json_value m_value = {}; #if JSON_DIAGNOSTICS /// a pointer to a parent value (for debugging purposes) basic_json* m_parent = nullptr; #endif ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// /// @name binary serialization/deserialization support /// @{ public: /*! @brief create a CBOR serialization of a given JSON value Serializes a given JSON value @a j to a byte vector using the CBOR (Concise Binary Object Representation) serialization format. CBOR is a binary serialization format which aims to be more compact than JSON itself, yet more efficient to parse. The library uses the following mapping from JSON values types to CBOR types according to the CBOR specification (RFC 7049): JSON value type | value/range | CBOR type | first byte --------------- | ------------------------------------------ | ---------------------------------- | --------------- null | `null` | Null | 0xF6 boolean | `true` | True | 0xF5 boolean | `false` | False | 0xF4 number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 number_integer | -24..-1 | Negative integer | 0x20..0x37 number_integer | 0..23 | Integer | 0x00..0x17 number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B number_unsigned | 0..23 | Integer | 0x00..0x17 number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B number_float | *any value representable by a float* | Single-Precision Float | 0xFA number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB string | *length*: 0..23 | UTF-8 string | 0x60..0x77 string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B array | *size*: 0..23 | array | 0x80..0x97 array | *size*: 23..255 | array (1 byte follow) | 0x98 array | *size*: 256..65535 | array (2 bytes follow) | 0x99 array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B object | *size*: 0..23 | map | 0xA0..0xB7 object | *size*: 23..255 | map (1 byte follow) | 0xB8 object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB binary | *size*: 0..23 | byte string | 0x40..0x57 binary | *size*: 23..255 | byte string (1 byte follow) | 0x58 binary | *size*: 256..65535 | byte string (2 bytes follow) | 0x59 binary | *size*: 65536..4294967295 | byte string (4 bytes follow) | 0x5A binary | *size*: 4294967296..18446744073709551615 | byte string (8 bytes follow) | 0x5B Binary values with subtype are mapped to tagged values (0xD8..0xDB) depending on the subtype, followed by a byte string, see "binary" cells in the table above. @note The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value. @note If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the @ref dump() function which serializes NaN or Infinity to `null`. @note The following CBOR types are not used in the conversion: - UTF-8 strings terminated by "break" (0x7F) - arrays terminated by "break" (0x9F) - maps terminated by "break" (0xBF) - byte strings terminated by "break" (0x5F) - date/time (0xC0..0xC1) - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) - half-precision floats (0xF9) - break (0xFF) @param[in] j JSON value to serialize @return CBOR serialization as byte vector @complexity Linear in the size of the JSON value @a j. @liveexample{The example shows the serialization of a JSON value to a byte vector in CBOR format.,to_cbor} @sa http://cbor.io @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the analogous deserialization @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format @since version 2.0.9; compact representation of floating-point numbers since version 3.8.0 */ static std::vector to_cbor(const basic_json& j) { std::vector result; to_cbor(j, result); return result; } static void to_cbor(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_cbor(j); } static void to_cbor(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_cbor(j); } /*! @brief create a MessagePack serialization of a given JSON value Serializes a given JSON value @a j to a byte vector using the MessagePack serialization format. MessagePack is a binary serialization format which aims to be more compact than JSON itself, yet more efficient to parse. The library uses the following mapping from JSON values types to MessagePack types according to the MessagePack specification: JSON value type | value/range | MessagePack type | first byte --------------- | --------------------------------- | ---------------- | ---------- null | `null` | nil | 0xC0 boolean | `true` | true | 0xC3 boolean | `false` | false | 0xC2 number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 number_integer | -2147483648..-32769 | int32 | 0xD2 number_integer | -32768..-129 | int16 | 0xD1 number_integer | -128..-33 | int8 | 0xD0 number_integer | -32..-1 | negative fixint | 0xE0..0xFF number_integer | 0..127 | positive fixint | 0x00..0x7F number_integer | 128..255 | uint 8 | 0xCC number_integer | 256..65535 | uint 16 | 0xCD number_integer | 65536..4294967295 | uint 32 | 0xCE number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF number_unsigned | 0..127 | positive fixint | 0x00..0x7F number_unsigned | 128..255 | uint 8 | 0xCC number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF number_float | *any value representable by a float* | float 32 | 0xCA number_float | *any value NOT representable by a float* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA string | *length*: 65536..4294967295 | str 32 | 0xDB array | *size*: 0..15 | fixarray | 0x90..0x9F array | *size*: 16..65535 | array 16 | 0xDC array | *size*: 65536..4294967295 | array 32 | 0xDD object | *size*: 0..15 | fix map | 0x80..0x8F object | *size*: 16..65535 | map 16 | 0xDE object | *size*: 65536..4294967295 | map 32 | 0xDF binary | *size*: 0..255 | bin 8 | 0xC4 binary | *size*: 256..65535 | bin 16 | 0xC5 binary | *size*: 65536..4294967295 | bin 32 | 0xC6 @note The mapping is **complete** in the sense that any JSON value type can be converted to a MessagePack value. @note The following values can **not** be converted to a MessagePack value: - strings with more than 4294967295 bytes - byte strings with more than 4294967295 bytes - arrays with more than 4294967295 elements - objects with more than 4294967295 elements @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. @note If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the @ref dump() function which serializes NaN or Infinity to `null`. @param[in] j JSON value to serialize @return MessagePack serialization as byte vector @complexity Linear in the size of the JSON value @a j. @liveexample{The example shows the serialization of a JSON value to a byte vector in MessagePack format.,to_msgpack} @sa http://msgpack.org @sa see @ref from_msgpack for the analogous deserialization @sa see @ref to_cbor(const basic_json& for the related CBOR format @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format @since version 2.0.9 */ static std::vector to_msgpack(const basic_json& j) { std::vector result; to_msgpack(j, result); return result; } static void to_msgpack(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_msgpack(j); } static void to_msgpack(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_msgpack(j); } /*! @brief create a UBJSON serialization of a given JSON value Serializes a given JSON value @a j to a byte vector using the UBJSON (Universal Binary JSON) serialization format. UBJSON aims to be more compact than JSON itself, yet more efficient to parse. The library uses the following mapping from JSON values types to UBJSON types according to the UBJSON specification: JSON value type | value/range | UBJSON type | marker --------------- | --------------------------------- | ----------- | ------ null | `null` | null | `Z` boolean | `true` | true | `T` boolean | `false` | false | `F` number_integer | -9223372036854775808..-2147483649 | int64 | `L` number_integer | -2147483648..-32769 | int32 | `l` number_integer | -32768..-129 | int16 | `I` number_integer | -128..127 | int8 | `i` number_integer | 128..255 | uint8 | `U` number_integer | 256..32767 | int16 | `I` number_integer | 32768..2147483647 | int32 | `l` number_integer | 2147483648..9223372036854775807 | int64 | `L` number_unsigned | 0..127 | int8 | `i` number_unsigned | 128..255 | uint8 | `U` number_unsigned | 256..32767 | int16 | `I` number_unsigned | 32768..2147483647 | int32 | `l` number_unsigned | 2147483648..9223372036854775807 | int64 | `L` number_unsigned | 2147483649..18446744073709551615 | high-precision | `H` number_float | *any value* | float64 | `D` string | *with shortest length indicator* | string | `S` array | *see notes on optimized format* | array | `[` object | *see notes on optimized format* | map | `{` @note The mapping is **complete** in the sense that any JSON value type can be converted to a UBJSON value. @note The following values can **not** be converted to a UBJSON value: - strings with more than 9223372036854775807 bytes (theoretical) @note The following markers are not used in the conversion: - `Z`: no-op values are not created. - `C`: single-byte strings are serialized with `S` markers. @note Any UBJSON output created @ref to_ubjson can be successfully parsed by @ref from_ubjson. @note If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the @ref dump() function which serializes NaN or Infinity to `null`. @note The optimized formats for containers are supported: Parameter @a use_size adds size information to the beginning of a container and removes the closing marker. Parameter @a use_type further checks whether all elements of a container have the same type and adds the type marker to the beginning of the container. The @a use_type parameter must only be used together with @a use_size = true. Note that @a use_size = true alone may result in larger representations - the benefit of this parameter is that the receiving side is immediately informed on the number of elements of the container. @note If the JSON data contains the binary type, the value stored is a list of integers, as suggested by the UBJSON documentation. In particular, this means that serialization and the deserialization of a JSON containing binary values into UBJSON and back will result in a different JSON object. @param[in] j JSON value to serialize @param[in] use_size whether to add size annotations to container types @param[in] use_type whether to add type annotations to container types (must be combined with @a use_size = true) @return UBJSON serialization as byte vector @complexity Linear in the size of the JSON value @a j. @liveexample{The example shows the serialization of a JSON value to a byte vector in UBJSON format.,to_ubjson} @sa http://ubjson.org @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the analogous deserialization @sa see @ref to_cbor(const basic_json& for the related CBOR format @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format @since version 3.1.0 */ static std::vector to_ubjson(const basic_json& j, const bool use_size = false, const bool use_type = false) { std::vector result; to_ubjson(j, result, use_size, use_type); return result; } static void to_ubjson(const basic_json& j, detail::output_adapter o, const bool use_size = false, const bool use_type = false) { binary_writer(o).write_ubjson(j, use_size, use_type); } static void to_ubjson(const basic_json& j, detail::output_adapter o, const bool use_size = false, const bool use_type = false) { binary_writer(o).write_ubjson(j, use_size, use_type); } /*! @brief Serializes the given JSON object `j` to BSON and returns a vector containing the corresponding BSON-representation. BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are stored as a single entity (a so-called document). The library uses the following mapping from JSON values types to BSON types: JSON value type | value/range | BSON type | marker --------------- | --------------------------------- | ----------- | ------ null | `null` | null | 0x0A boolean | `true`, `false` | boolean | 0x08 number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 number_integer | -2147483648..2147483647 | int32 | 0x10 number_integer | 2147483648..9223372036854775807 | int64 | 0x12 number_unsigned | 0..2147483647 | int32 | 0x10 number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 number_unsigned | 9223372036854775808..18446744073709551615| -- | -- number_float | *any value* | double | 0x01 string | *any value* | string | 0x02 array | *any value* | document | 0x04 object | *any value* | document | 0x03 binary | *any value* | binary | 0x05 @warning The mapping is **incomplete**, since only JSON-objects (and things contained therein) can be serialized to BSON. Also, integers larger than 9223372036854775807 cannot be serialized to BSON, and the keys may not contain U+0000, since they are serialized a zero-terminated c-strings. @throw out_of_range.407 if `j.is_number_unsigned() && j.get() > 9223372036854775807` @throw out_of_range.409 if a key in `j` contains a NULL (U+0000) @throw type_error.317 if `!j.is_object()` @pre The input `j` is required to be an object: `j.is_object() == true`. @note Any BSON output created via @ref to_bson can be successfully parsed by @ref from_bson. @param[in] j JSON value to serialize @return BSON serialization as byte vector @complexity Linear in the size of the JSON value @a j. @liveexample{The example shows the serialization of a JSON value to a byte vector in BSON format.,to_bson} @sa http://bsonspec.org/spec.html @sa see @ref from_bson(detail::input_adapter&&, const bool strict) for the analogous deserialization @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format @sa see @ref to_cbor(const basic_json&) for the related CBOR format @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format */ static std::vector to_bson(const basic_json& j) { std::vector result; to_bson(j, result); return result; } /*! @brief Serializes the given JSON object `j` to BSON and forwards the corresponding BSON-representation to the given output_adapter `o`. @param j The JSON object to convert to BSON. @param o The output adapter that receives the binary BSON representation. @pre The input `j` shall be an object: `j.is_object() == true` @sa see @ref to_bson(const basic_json&) */ static void to_bson(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_bson(j); } /*! @copydoc to_bson(const basic_json&, detail::output_adapter) */ static void to_bson(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_bson(j); } /*! @brief create a JSON value from an input in CBOR format Deserializes a given input @a i to a JSON value using the CBOR (Concise Binary Object Representation) serialization format. The library maps CBOR types to JSON value types as follows: CBOR type | JSON value type | first byte ---------------------- | --------------- | ---------- Integer | number_unsigned | 0x00..0x17 Unsigned integer | number_unsigned | 0x18 Unsigned integer | number_unsigned | 0x19 Unsigned integer | number_unsigned | 0x1A Unsigned integer | number_unsigned | 0x1B Negative integer | number_integer | 0x20..0x37 Negative integer | number_integer | 0x38 Negative integer | number_integer | 0x39 Negative integer | number_integer | 0x3A Negative integer | number_integer | 0x3B Byte string | binary | 0x40..0x57 Byte string | binary | 0x58 Byte string | binary | 0x59 Byte string | binary | 0x5A Byte string | binary | 0x5B UTF-8 string | string | 0x60..0x77 UTF-8 string | string | 0x78 UTF-8 string | string | 0x79 UTF-8 string | string | 0x7A UTF-8 string | string | 0x7B UTF-8 string | string | 0x7F array | array | 0x80..0x97 array | array | 0x98 array | array | 0x99 array | array | 0x9A array | array | 0x9B array | array | 0x9F map | object | 0xA0..0xB7 map | object | 0xB8 map | object | 0xB9 map | object | 0xBA map | object | 0xBB map | object | 0xBF False | `false` | 0xF4 True | `true` | 0xF5 Null | `null` | 0xF6 Half-Precision Float | number_float | 0xF9 Single-Precision Float | number_float | 0xFA Double-Precision Float | number_float | 0xFB @warning The mapping is **incomplete** in the sense that not all CBOR types can be converted to a JSON value. The following CBOR types are not supported and will yield parse errors (parse_error.112): - date/time (0xC0..0xC1) - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) @warning CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected (parse_error.113). @note Any CBOR output created @ref to_cbor can be successfully parsed by @ref from_cbor. @param[in] i an input in CBOR format convertible to an input adapter @param[in] strict whether to expect the input to be consumed until EOF (true by default) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) @param[in] tag_handler how to treat CBOR tags (optional, error by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be value_t::discarded. @throw parse_error.110 if the given input ends prematurely or the end of file was not reached when @a strict was set to true @throw parse_error.112 if unsupported features from CBOR were used in the given input @a v or if the input is not valid CBOR @throw parse_error.113 if a string was expected as map key, but not found @complexity Linear in the size of the input @a i. @liveexample{The example shows the deserialization of a byte vector in CBOR format to a JSON value.,from_cbor} @sa http://cbor.io @sa see @ref to_cbor(const basic_json&) for the analogous serialization @sa see @ref from_msgpack(InputType&&, const bool, const bool) for the related MessagePack format @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the related UBJSON format @since version 2.0.9; parameter @a start_index since 2.1.1; changed to consume input adapters, removed start_index parameter, and added @a strict parameter since 3.0.0; added @a allow_exceptions parameter since 3.2.0; added @a tag_handler parameter since 3.9.0. */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_cbor(InputType&& i, const bool strict = true, const bool allow_exceptions = true, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } /*! @copydoc from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_cbor(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } template JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) static basic_json from_cbor(const T* ptr, std::size_t len, const bool strict = true, const bool allow_exceptions = true, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) static basic_json from_cbor(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true, const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } /*! @brief create a JSON value from an input in MessagePack format Deserializes a given input @a i to a JSON value using the MessagePack serialization format. The library maps MessagePack types to JSON value types as follows: MessagePack type | JSON value type | first byte ---------------- | --------------- | ---------- positive fixint | number_unsigned | 0x00..0x7F fixmap | object | 0x80..0x8F fixarray | array | 0x90..0x9F fixstr | string | 0xA0..0xBF nil | `null` | 0xC0 false | `false` | 0xC2 true | `true` | 0xC3 float 32 | number_float | 0xCA float 64 | number_float | 0xCB uint 8 | number_unsigned | 0xCC uint 16 | number_unsigned | 0xCD uint 32 | number_unsigned | 0xCE uint 64 | number_unsigned | 0xCF int 8 | number_integer | 0xD0 int 16 | number_integer | 0xD1 int 32 | number_integer | 0xD2 int 64 | number_integer | 0xD3 str 8 | string | 0xD9 str 16 | string | 0xDA str 32 | string | 0xDB array 16 | array | 0xDC array 32 | array | 0xDD map 16 | object | 0xDE map 32 | object | 0xDF bin 8 | binary | 0xC4 bin 16 | binary | 0xC5 bin 32 | binary | 0xC6 ext 8 | binary | 0xC7 ext 16 | binary | 0xC8 ext 32 | binary | 0xC9 fixext 1 | binary | 0xD4 fixext 2 | binary | 0xD5 fixext 4 | binary | 0xD6 fixext 8 | binary | 0xD7 fixext 16 | binary | 0xD8 negative fixint | number_integer | 0xE0-0xFF @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. @param[in] i an input in MessagePack format convertible to an input adapter @param[in] strict whether to expect the input to be consumed until EOF (true by default) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be value_t::discarded. @throw parse_error.110 if the given input ends prematurely or the end of file was not reached when @a strict was set to true @throw parse_error.112 if unsupported features from MessagePack were used in the given input @a i or if the input is not valid MessagePack @throw parse_error.113 if a string was expected as map key, but not found @complexity Linear in the size of the input @a i. @liveexample{The example shows the deserialization of a byte vector in MessagePack format to a JSON value.,from_msgpack} @sa http://msgpack.org @sa see @ref to_msgpack(const basic_json&) for the analogous serialization @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the related CBOR format @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the related UBJSON format @sa see @ref from_bson(InputType&&, const bool, const bool) for the related BSON format @since version 2.0.9; parameter @a start_index since 2.1.1; changed to consume input adapters, removed start_index parameter, and added @a strict parameter since 3.0.0; added @a allow_exceptions parameter since 3.2.0 */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_msgpack(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @copydoc from_msgpack(InputType&&, const bool, const bool) */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_msgpack(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } template JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) static basic_json from_msgpack(const T* ptr, std::size_t len, const bool strict = true, const bool allow_exceptions = true) { return from_msgpack(ptr, ptr + len, strict, allow_exceptions); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) static basic_json from_msgpack(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @brief create a JSON value from an input in UBJSON format Deserializes a given input @a i to a JSON value using the UBJSON (Universal Binary JSON) serialization format. The library maps UBJSON types to JSON value types as follows: UBJSON type | JSON value type | marker ----------- | --------------------------------------- | ------ no-op | *no value, next value is read* | `N` null | `null` | `Z` false | `false` | `F` true | `true` | `T` float32 | number_float | `d` float64 | number_float | `D` uint8 | number_unsigned | `U` int8 | number_integer | `i` int16 | number_integer | `I` int32 | number_integer | `l` int64 | number_integer | `L` high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H' string | string | `S` char | string | `C` array | array (optimized values are supported) | `[` object | object (optimized values are supported) | `{` @note The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value. @param[in] i an input in UBJSON format convertible to an input adapter @param[in] strict whether to expect the input to be consumed until EOF (true by default) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be value_t::discarded. @throw parse_error.110 if the given input ends prematurely or the end of file was not reached when @a strict was set to true @throw parse_error.112 if a parse error occurs @throw parse_error.113 if a string could not be parsed successfully @complexity Linear in the size of the input @a i. @liveexample{The example shows the deserialization of a byte vector in UBJSON format to a JSON value.,from_ubjson} @sa http://ubjson.org @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the analogous serialization @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the related CBOR format @sa see @ref from_msgpack(InputType&&, const bool, const bool) for the related MessagePack format @sa see @ref from_bson(InputType&&, const bool, const bool) for the related BSON format @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_ubjson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @copydoc from_ubjson(InputType&&, const bool, const bool) */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_ubjson(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } template JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) static basic_json from_ubjson(const T* ptr, std::size_t len, const bool strict = true, const bool allow_exceptions = true) { return from_ubjson(ptr, ptr + len, strict, allow_exceptions); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) static basic_json from_ubjson(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @brief Create a JSON value from an input in BSON format Deserializes a given input @a i to a JSON value using the BSON (Binary JSON) serialization format. The library maps BSON record types to JSON value types as follows: BSON type | BSON marker byte | JSON value type --------------- | ---------------- | --------------------------- double | 0x01 | number_float string | 0x02 | string document | 0x03 | object array | 0x04 | array binary | 0x05 | binary undefined | 0x06 | still unsupported ObjectId | 0x07 | still unsupported boolean | 0x08 | boolean UTC Date-Time | 0x09 | still unsupported null | 0x0A | null Regular Expr. | 0x0B | still unsupported DB Pointer | 0x0C | still unsupported JavaScript Code | 0x0D | still unsupported Symbol | 0x0E | still unsupported JavaScript Code | 0x0F | still unsupported int32 | 0x10 | number_integer Timestamp | 0x11 | still unsupported 128-bit decimal float | 0x13 | still unsupported Max Key | 0x7F | still unsupported Min Key | 0xFF | still unsupported @warning The mapping is **incomplete**. The unsupported mappings are indicated in the table above. @param[in] i an input in BSON format convertible to an input adapter @param[in] strict whether to expect the input to be consumed until EOF (true by default) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be value_t::discarded. @throw parse_error.114 if an unsupported BSON record type is encountered @complexity Linear in the size of the input @a i. @liveexample{The example shows the deserialization of a byte vector in BSON format to a JSON value.,from_bson} @sa http://bsonspec.org/spec.html @sa see @ref to_bson(const basic_json&) for the analogous serialization @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the related CBOR format @sa see @ref from_msgpack(InputType&&, const bool, const bool) for the related MessagePack format @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the related UBJSON format */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_bson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /*! @copydoc from_bson(InputType&&, const bool, const bool) */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_bson(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } template JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) static basic_json from_bson(const T* ptr, std::size_t len, const bool strict = true, const bool allow_exceptions = true) { return from_bson(ptr, ptr + len, strict, allow_exceptions); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) static basic_json from_bson(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /// @} ////////////////////////// // JSON Pointer support // ////////////////////////// /// @name JSON Pointer functions /// @{ /*! @brief access specified element via JSON Pointer Uses a JSON pointer to retrieve a reference to the respective JSON value. No bound checking is performed. Similar to @ref operator[](const typename object_t::key_type&), `null` values are created in arrays and objects if necessary. In particular: - If the JSON pointer points to an object key that does not exist, it is created an filled with a `null` value before a reference to it is returned. - If the JSON pointer points to an array index that does not exist, it is created an filled with a `null` value before a reference to it is returned. All indices between the current maximum and the given index are also filled with `null`. - The special value `-` is treated as a synonym for the index past the end. @param[in] ptr a JSON pointer @return reference to the element pointed to by @a ptr @complexity Constant. @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.404 if the JSON pointer can not be resolved @liveexample{The behavior is shown in the example.,operatorjson_pointer} @since version 2.0.0 */ reference operator[](const json_pointer& ptr) { return ptr.get_unchecked(this); } /*! @brief access specified element via JSON Pointer Uses a JSON pointer to retrieve a reference to the respective JSON value. No bound checking is performed. The function does not change the JSON value; no `null` values are created. In particular, the special value `-` yields an exception. @param[in] ptr JSON pointer to the desired element @return const reference to the element pointed to by @a ptr @complexity Constant. @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} @since version 2.0.0 */ const_reference operator[](const json_pointer& ptr) const { return ptr.get_unchecked(this); } /*! @brief access specified element via JSON Pointer Returns a reference to the element at with specified JSON pointer @a ptr, with bounds checking. @param[in] ptr JSON pointer to the desired element @return reference to the element pointed to by @a ptr @throw parse_error.106 if an array index in the passed JSON pointer @a ptr begins with '0'. See example below. @throw parse_error.109 if an array index in the passed JSON pointer @a ptr is not a number. See example below. @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr is out of range. See example below. @throw out_of_range.402 if the array index '-' is used in the passed JSON pointer @a ptr. As `at` provides checked access (and no elements are implicitly inserted), the index '-' is always invalid. See example below. @throw out_of_range.403 if the JSON pointer describes a key of an object which cannot be found. See example below. @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 2.0.0 @liveexample{The behavior is shown in the example.,at_json_pointer} */ reference at(const json_pointer& ptr) { return ptr.get_checked(this); } /*! @brief access specified element via JSON Pointer Returns a const reference to the element at with specified JSON pointer @a ptr, with bounds checking. @param[in] ptr JSON pointer to the desired element @return reference to the element pointed to by @a ptr @throw parse_error.106 if an array index in the passed JSON pointer @a ptr begins with '0'. See example below. @throw parse_error.109 if an array index in the passed JSON pointer @a ptr is not a number. See example below. @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr is out of range. See example below. @throw out_of_range.402 if the array index '-' is used in the passed JSON pointer @a ptr. As `at` provides checked access (and no elements are implicitly inserted), the index '-' is always invalid. See example below. @throw out_of_range.403 if the JSON pointer describes a key of an object which cannot be found. See example below. @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 2.0.0 @liveexample{The behavior is shown in the example.,at_json_pointer_const} */ const_reference at(const json_pointer& ptr) const { return ptr.get_checked(this); } /*! @brief return flattened JSON value The function creates a JSON object whose keys are JSON pointers (see [RFC 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all primitive. The original JSON value can be restored using the @ref unflatten() function. @return an object that maps JSON pointers to primitive values @note Empty objects and arrays are flattened to `null` and will not be reconstructed correctly by the @ref unflatten() function. @complexity Linear in the size the JSON value. @liveexample{The following code shows how a JSON object is flattened to an object whose keys consist of JSON pointers.,flatten} @sa see @ref unflatten() for the reverse function @since version 2.0.0 */ basic_json flatten() const { basic_json result(value_t::object); json_pointer::flatten("", *this, result); return result; } /*! @brief unflatten a previously flattened JSON value The function restores the arbitrary nesting of a JSON value that has been flattened before using the @ref flatten() function. The JSON value must meet certain constraints: 1. The value must be an object. 2. The keys must be JSON pointers (see [RFC 6901](https://tools.ietf.org/html/rfc6901)) 3. The mapped values must be primitive JSON types. @return the original JSON from a flattened version @note Empty objects and arrays are flattened by @ref flatten() to `null` values and can not unflattened to their original type. Apart from this example, for a JSON value `j`, the following is always true: `j == j.flatten().unflatten()`. @complexity Linear in the size the JSON value. @throw type_error.314 if value is not an object @throw type_error.315 if object values are not primitive @liveexample{The following code shows how a flattened JSON object is unflattened into the original nested JSON object.,unflatten} @sa see @ref flatten() for the reverse function @since version 2.0.0 */ basic_json unflatten() const { return json_pointer::unflatten(*this); } /// @} ////////////////////////// // JSON Patch functions // ////////////////////////// /// @name JSON Patch functions /// @{ /*! @brief applies a JSON patch [JSON Patch](http://jsonpatch.com) defines a JSON document structure for expressing a sequence of operations to apply to a JSON) document. With this function, a JSON Patch is applied to the current JSON value by executing all operations from the patch. @param[in] json_patch JSON patch document @return patched document @note The application of a patch is atomic: Either all operations succeed and the patched document is returned or an exception is thrown. In any case, the original value is not changed: the patch is applied to a copy of the value. @throw parse_error.104 if the JSON patch does not consist of an array of objects @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory attributes are missing); example: `"operation add must have member path"` @throw out_of_range.401 if an array index is out of range. @throw out_of_range.403 if a JSON pointer inside the patch could not be resolved successfully in the current JSON value; example: `"key baz not found"` @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", "move") @throw other_error.501 if "test" operation was unsuccessful @complexity Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is affected by the patch, the complexity can usually be neglected. @liveexample{The following code shows how a JSON patch is applied to a value.,patch} @sa see @ref diff -- create a JSON patch by comparing two JSON values @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) @since version 2.0.0 */ basic_json patch(const basic_json& json_patch) const { // make a working copy to apply the patch to basic_json result = *this; // the valid JSON Patch operations enum class patch_operations {add, remove, replace, move, copy, test, invalid}; const auto get_op = [](const std::string & op) { if (op == "add") { return patch_operations::add; } if (op == "remove") { return patch_operations::remove; } if (op == "replace") { return patch_operations::replace; } if (op == "move") { return patch_operations::move; } if (op == "copy") { return patch_operations::copy; } if (op == "test") { return patch_operations::test; } return patch_operations::invalid; }; // wrapper for "add" operation; add value at ptr const auto operation_add = [&result](json_pointer & ptr, basic_json val) { // adding to the root of the target document means replacing it if (ptr.empty()) { result = val; return; } // make sure the top element of the pointer exists json_pointer top_pointer = ptr.top(); if (top_pointer != ptr) { result.at(top_pointer); } // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); ptr.pop_back(); basic_json& parent = result[ptr]; switch (parent.m_type) { case value_t::null: case value_t::object: { // use operator[] to add value parent[last_path] = val; break; } case value_t::array: { if (last_path == "-") { // special case: append to back parent.push_back(val); } else { const auto idx = json_pointer::array_index(last_path); if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent)); } // default case: insert add offset parent.insert(parent.begin() + static_cast(idx), val); } break; } // if there exists a parent it cannot be primitive case value_t::string: // LCOV_EXCL_LINE case value_t::boolean: // LCOV_EXCL_LINE case value_t::number_integer: // LCOV_EXCL_LINE case value_t::number_unsigned: // LCOV_EXCL_LINE case value_t::number_float: // LCOV_EXCL_LINE case value_t::binary: // LCOV_EXCL_LINE case value_t::discarded: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } }; // wrapper for "remove" operation; remove value at ptr const auto operation_remove = [this, &result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); ptr.pop_back(); basic_json& parent = result.at(ptr); // remove child if (parent.is_object()) { // perform range check auto it = parent.find(last_path); if (JSON_HEDLEY_LIKELY(it != parent.end())) { parent.erase(it); } else { JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); } } else if (parent.is_array()) { // note erase performs range check parent.erase(json_pointer::array_index(last_path)); } }; // type check: top level value must be an array if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); } // iterate and apply the operations for (const auto& val : json_patch) { // wrapper to get a value for an operation const auto get_value = [&val](const std::string & op, const std::string & member, bool string_type) -> basic_json & { // find value auto it = val.m_value.object->find(member); // context-sensitive error message const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; // check if desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); } // check if result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); } // no error: return value return it->second; }; // type check: every element of the array must be an object if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); } // collect mandatory members const auto op = get_value("op", "op", true).template get(); const auto path = get_value(op, "path", true).template get(); json_pointer ptr(path); switch (get_op(op)) { case patch_operations::add: { operation_add(ptr, get_value("add", "value", false)); break; } case patch_operations::remove: { operation_remove(ptr); break; } case patch_operations::replace: { // the "path" location must exist - use at() result.at(ptr) = get_value("replace", "value", false); break; } case patch_operations::move: { const auto from_path = get_value("move", "from", true).template get(); json_pointer from_ptr(from_path); // the "from" location must exist - use at() basic_json v = result.at(from_ptr); // The move operation is functionally identical to a // "remove" operation on the "from" location, followed // immediately by an "add" operation at the target // location with the value that was just removed. operation_remove(from_ptr); operation_add(ptr, v); break; } case patch_operations::copy: { const auto from_path = get_value("copy", "from", true).template get(); const json_pointer from_ptr(from_path); // the "from" location must exist - use at() basic_json v = result.at(from_ptr); // The copy is functionally identical to an "add" // operation at the target location using the value // specified in the "from" member. operation_add(ptr, v); break; } case patch_operations::test: { bool success = false; JSON_TRY { // check if "value" matches the one at "path" // the "path" location must exist - use at() success = (result.at(ptr) == get_value("test", "value", false)); } JSON_INTERNAL_CATCH (out_of_range&) { // ignore out of range errors: success remains false } // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); } break; } case patch_operations::invalid: default: { // op must be "add", "remove", "replace", "move", "copy", or // "test" JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); } } } return result; } /*! @brief creates a diff as a JSON patch Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can be changed into the value @a target by calling @ref patch function. @invariant For two JSON values @a source and @a target, the following code yields always `true`: @code {.cpp} source.patch(diff(source, target)) == target; @endcode @note Currently, only `remove`, `add`, and `replace` operations are generated. @param[in] source JSON value to compare from @param[in] target JSON value to compare against @param[in] path helper value to create JSON pointers @return a JSON patch to convert the @a source to @a target @complexity Linear in the lengths of @a source and @a target. @liveexample{The following code shows how a JSON patch is created as a diff for two JSON values.,diff} @sa see @ref patch -- apply a JSON patch @sa see @ref merge_patch -- apply a JSON Merge Patch @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) @since version 2.0.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json diff(const basic_json& source, const basic_json& target, const std::string& path = "") { // the patch basic_json result(value_t::array); // if the values are the same, return empty patch if (source == target) { return result; } if (source.type() != target.type()) { // different types: replace value result.push_back( { {"op", "replace"}, {"path", path}, {"value", target} }); return result; } switch (source.type()) { case value_t::array: { // first pass: traverse common elements std::size_t i = 0; while (i < source.size() && i < target.size()) { // recursive call to compare array values at index i auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); ++i; } // i now reached the end of at least one array // in a second pass, traverse the remaining elements // remove my remaining elements const auto end_index = static_cast(result.size()); while (i < source.size()) { // add operations in reverse order to avoid invalid // indices result.insert(result.begin() + end_index, object( { {"op", "remove"}, {"path", path + "/" + std::to_string(i)} })); ++i; } // add other remaining elements while (i < target.size()) { result.push_back( { {"op", "add"}, {"path", path + "/-"}, {"value", target[i]} }); ++i; } break; } case value_t::object: { // first pass: traverse this object's elements for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch const auto path_key = path + "/" + detail::escape(it.key()); if (target.find(it.key()) != target.end()) { // recursive call to compare object values at key it auto temp_diff = diff(it.value(), target[it.key()], path_key); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); } else { // found a key that is not in o -> remove it result.push_back(object( { {"op", "remove"}, {"path", path_key} })); } } // second pass: traverse other object's elements for (auto it = target.cbegin(); it != target.cend(); ++it) { if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it const auto path_key = path + "/" + detail::escape(it.key()); result.push_back( { {"op", "add"}, {"path", path_key}, {"value", it.value()} }); } } break; } case value_t::null: case value_t::string: case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::binary: case value_t::discarded: default: { // both primitive type: replace value result.push_back( { {"op", "replace"}, {"path", path}, {"value", target} }); break; } } return result; } /// @} //////////////////////////////// // JSON Merge Patch functions // //////////////////////////////// /// @name JSON Merge Patch functions /// @{ /*! @brief applies a JSON Merge Patch The merge patch format is primarily intended for use with the HTTP PATCH method as a means of describing a set of modifications to a target resource's content. This function applies a merge patch to the current JSON value. The function implements the following algorithm from Section 2 of [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): ``` define MergePatch(Target, Patch): if Patch is an Object: if Target is not an Object: Target = {} // Ignore the contents and set it to an empty Object for each Name/Value pair in Patch: if Value is null: if Name exists in Target: remove the Name/Value pair from Target else: Target[Name] = MergePatch(Target[Name], Value) return Target else: return Patch ``` Thereby, `Target` is the current object; that is, the patch is applied to the current value. @param[in] apply_patch the patch to apply @complexity Linear in the lengths of @a patch. @liveexample{The following code shows how a JSON Merge Patch is applied to a JSON document.,merge_patch} @sa see @ref patch -- apply a JSON patch @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) @since version 3.0.0 */ void merge_patch(const basic_json& apply_patch) { if (apply_patch.is_object()) { if (!is_object()) { *this = object(); } for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) { if (it.value().is_null()) { erase(it.key()); } else { operator[](it.key()).merge_patch(it.value()); } } } else { *this = apply_patch; } } /// @} }; /*! @brief user-defined to_string function for JSON values This function implements a user-defined to_string for JSON objects. @param[in] j a JSON object @return a std::string object */ NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) { return j.dump(); } } // namespace nlohmann /////////////////////// // nonmember support // /////////////////////// // specialization of std::swap, and std::hash namespace std { /// hash value for JSON objects template<> struct hash { /*! @brief return a hash value for a JSON object @since version 1.0.0 */ std::size_t operator()(const nlohmann::json& j) const { return nlohmann::detail::hash(j); } }; /// specialization for std::less /// @note: do not remove the space after '<', /// see https://github.com/nlohmann/json/pull/679 template<> struct less<::nlohmann::detail::value_t> { /*! @brief compare two value_t enum values @since version 3.0.0 */ bool operator()(nlohmann::detail::value_t lhs, nlohmann::detail::value_t rhs) const noexcept { return nlohmann::detail::operator<(lhs, rhs); } }; // C++20 prohibit function specialization in the std namespace. #ifndef JSON_HAS_CPP_20 /*! @brief exchanges the values of two JSON objects @since version 1.0.0 */ template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) is_nothrow_move_assignable::value ) { j1.swap(j2); } #endif } // namespace std /*! @brief user-defined string literal for JSON values This operator implements a user-defined string literal for JSON objects. It can be used by adding `"_json"` to a string literal and returns a JSON object if no parse error occurred. @param[in] s a string representation of a JSON object @param[in] n the length of string @a s @return a JSON object @since version 1.0.0 */ JSON_HEDLEY_NON_NULL(1) inline nlohmann::json operator "" _json(const char* s, std::size_t n) { return nlohmann::json::parse(s, s + n); } /*! @brief user-defined string literal for JSON pointer This operator implements a user-defined string literal for JSON Pointers. It can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer object if no parse error occurred. @param[in] s a string representation of a JSON Pointer @param[in] n the length of string @a s @return a JSON pointer object @since version 2.0.0 */ JSON_HEDLEY_NON_NULL(1) inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { return nlohmann::json::json_pointer(std::string(s, n)); } // #include // restore clang diagnostic settings #if defined(__clang__) #pragma clang diagnostic pop #endif // clean up #undef JSON_ASSERT #undef JSON_INTERNAL_CATCH #undef JSON_CATCH #undef JSON_THROW #undef JSON_TRY #undef JSON_PRIVATE_UNLESS_TESTED #undef JSON_HAS_CPP_11 #undef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_17 #undef JSON_HAS_CPP_20 #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL // #include #undef JSON_HEDLEY_ALWAYS_INLINE #undef JSON_HEDLEY_ARM_VERSION #undef JSON_HEDLEY_ARM_VERSION_CHECK #undef JSON_HEDLEY_ARRAY_PARAM #undef JSON_HEDLEY_ASSUME #undef JSON_HEDLEY_BEGIN_C_DECLS #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE #undef JSON_HEDLEY_CLANG_HAS_BUILTIN #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE #undef JSON_HEDLEY_CLANG_HAS_EXTENSION #undef JSON_HEDLEY_CLANG_HAS_FEATURE #undef JSON_HEDLEY_CLANG_HAS_WARNING #undef JSON_HEDLEY_COMPCERT_VERSION #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK #undef JSON_HEDLEY_CONCAT #undef JSON_HEDLEY_CONCAT3 #undef JSON_HEDLEY_CONCAT3_EX #undef JSON_HEDLEY_CONCAT_EX #undef JSON_HEDLEY_CONST #undef JSON_HEDLEY_CONSTEXPR #undef JSON_HEDLEY_CONST_CAST #undef JSON_HEDLEY_CPP_CAST #undef JSON_HEDLEY_CRAY_VERSION #undef JSON_HEDLEY_CRAY_VERSION_CHECK #undef JSON_HEDLEY_C_DECL #undef JSON_HEDLEY_DEPRECATED #undef JSON_HEDLEY_DEPRECATED_FOR #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION #undef JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_DIAGNOSTIC_PUSH #undef JSON_HEDLEY_DMC_VERSION #undef JSON_HEDLEY_DMC_VERSION_CHECK #undef JSON_HEDLEY_EMPTY_BASES #undef JSON_HEDLEY_EMSCRIPTEN_VERSION #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK #undef JSON_HEDLEY_END_C_DECLS #undef JSON_HEDLEY_FLAGS #undef JSON_HEDLEY_FLAGS_CAST #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE #undef JSON_HEDLEY_GCC_HAS_BUILTIN #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE #undef JSON_HEDLEY_GCC_HAS_EXTENSION #undef JSON_HEDLEY_GCC_HAS_FEATURE #undef JSON_HEDLEY_GCC_HAS_WARNING #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK #undef JSON_HEDLEY_GCC_VERSION #undef JSON_HEDLEY_GCC_VERSION_CHECK #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE #undef JSON_HEDLEY_GNUC_HAS_BUILTIN #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE #undef JSON_HEDLEY_GNUC_HAS_EXTENSION #undef JSON_HEDLEY_GNUC_HAS_FEATURE #undef JSON_HEDLEY_GNUC_HAS_WARNING #undef JSON_HEDLEY_GNUC_VERSION #undef JSON_HEDLEY_GNUC_VERSION_CHECK #undef JSON_HEDLEY_HAS_ATTRIBUTE #undef JSON_HEDLEY_HAS_BUILTIN #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE #undef JSON_HEDLEY_HAS_EXTENSION #undef JSON_HEDLEY_HAS_FEATURE #undef JSON_HEDLEY_HAS_WARNING #undef JSON_HEDLEY_IAR_VERSION #undef JSON_HEDLEY_IAR_VERSION_CHECK #undef JSON_HEDLEY_IBM_VERSION #undef JSON_HEDLEY_IBM_VERSION_CHECK #undef JSON_HEDLEY_IMPORT #undef JSON_HEDLEY_INLINE #undef JSON_HEDLEY_INTEL_CL_VERSION #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK #undef JSON_HEDLEY_INTEL_VERSION #undef JSON_HEDLEY_INTEL_VERSION_CHECK #undef JSON_HEDLEY_IS_CONSTANT #undef JSON_HEDLEY_IS_CONSTEXPR_ #undef JSON_HEDLEY_LIKELY #undef JSON_HEDLEY_MALLOC #undef JSON_HEDLEY_MCST_LCC_VERSION #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK #undef JSON_HEDLEY_MESSAGE #undef JSON_HEDLEY_MSVC_VERSION #undef JSON_HEDLEY_MSVC_VERSION_CHECK #undef JSON_HEDLEY_NEVER_INLINE #undef JSON_HEDLEY_NON_NULL #undef JSON_HEDLEY_NO_ESCAPE #undef JSON_HEDLEY_NO_RETURN #undef JSON_HEDLEY_NO_THROW #undef JSON_HEDLEY_NULL #undef JSON_HEDLEY_PELLES_VERSION #undef JSON_HEDLEY_PELLES_VERSION_CHECK #undef JSON_HEDLEY_PGI_VERSION #undef JSON_HEDLEY_PGI_VERSION_CHECK #undef JSON_HEDLEY_PREDICT #undef JSON_HEDLEY_PRINTF_FORMAT #undef JSON_HEDLEY_PRIVATE #undef JSON_HEDLEY_PUBLIC #undef JSON_HEDLEY_PURE #undef JSON_HEDLEY_REINTERPRET_CAST #undef JSON_HEDLEY_REQUIRE #undef JSON_HEDLEY_REQUIRE_CONSTEXPR #undef JSON_HEDLEY_REQUIRE_MSG #undef JSON_HEDLEY_RESTRICT #undef JSON_HEDLEY_RETURNS_NON_NULL #undef JSON_HEDLEY_SENTINEL #undef JSON_HEDLEY_STATIC_ASSERT #undef JSON_HEDLEY_STATIC_CAST #undef JSON_HEDLEY_STRINGIFY #undef JSON_HEDLEY_STRINGIFY_EX #undef JSON_HEDLEY_SUNPRO_VERSION #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK #undef JSON_HEDLEY_TINYC_VERSION #undef JSON_HEDLEY_TINYC_VERSION_CHECK #undef JSON_HEDLEY_TI_ARMCL_VERSION #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK #undef JSON_HEDLEY_TI_CL2000_VERSION #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK #undef JSON_HEDLEY_TI_CL430_VERSION #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK #undef JSON_HEDLEY_TI_CL6X_VERSION #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK #undef JSON_HEDLEY_TI_CL7X_VERSION #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK #undef JSON_HEDLEY_TI_CLPRU_VERSION #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK #undef JSON_HEDLEY_TI_VERSION #undef JSON_HEDLEY_TI_VERSION_CHECK #undef JSON_HEDLEY_UNAVAILABLE #undef JSON_HEDLEY_UNLIKELY #undef JSON_HEDLEY_UNPREDICTABLE #undef JSON_HEDLEY_UNREACHABLE #undef JSON_HEDLEY_UNREACHABLE_RETURN #undef JSON_HEDLEY_VERSION #undef JSON_HEDLEY_VERSION_DECODE_MAJOR #undef JSON_HEDLEY_VERSION_DECODE_MINOR #undef JSON_HEDLEY_VERSION_DECODE_REVISION #undef JSON_HEDLEY_VERSION_ENCODE #undef JSON_HEDLEY_WARNING #undef JSON_HEDLEY_WARN_UNUSED_RESULT #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG #undef JSON_HEDLEY_FALL_THROUGH #endif // INCLUDE_NLOHMANN_JSON_HPP_ ================================================ FILE: d2mapapi/mapdata.cpp ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #include "mapdata.h" #include "offset.h" #include "d2map.h" #include "d2ptrs.h" #include #include #include namespace d2mapapi { constexpr int unit_type_npc = 1; constexpr int unit_type_object = 2; constexpr int unit_type_tile = 5; MapData::MapData(Act *act, unsigned int areaId, bool generatePathData) : CollisionMap(areaId) { auto d2Ver = getD2Version(); Level *pLevel = getLevel(act, areaId); if (!pLevel) { return; } unsigned int currLevelNo; unsigned int currPosX; unsigned int currPosY; unsigned int width, height; if (!pLevel->pRoom2First(d2Ver)) { D2COMMON_InitLevel(pLevel); } built = true; if (!pLevel->pRoom2First(d2Ver)) { return; } currLevelNo = pLevel->dwLevelNo(d2Ver); currPosX = pLevel->dwPosX(d2Ver); currPosY = pLevel->dwPosY(d2Ver); width = pLevel->dwSizeX(d2Ver) * 5; height = pLevel->dwSizeY(d2Ver) * 5; offset.x = currPosX * 5; offset.y = currPosY * 5; auto map = std::vector(width * height, -1); size.width = width; size.height = height; std::vector> sides; for (Room2 *pRoom2 = pLevel->pRoom2First(d2Ver); pRoom2; pRoom2 = pRoom2->pRoom2Next(d2Ver)) { bool bAdded = false; auto roomPosX = pRoom2->dwPosX(d2Ver); auto roomPosY = pRoom2->dwPosY(d2Ver); if (!pRoom2->pRoom1(d2Ver)) { bAdded = true; D2COMMON_AddRoomData(act, pLevel->dwLevelNo(d2Ver), roomPosX, roomPosY, nullptr); } /* Check near levels' walkable rect (we check 2 pixels from edge) * side: 0-left 1-right 2-top 3-bottom */ for (uint32_t i = 0; i < pRoom2->dwRoomsNear(d2Ver); i++) { int side = -1; auto *pRoom2Near = pRoom2->pRoom2Near(d2Ver)[i]; auto nearLevelNo = pRoom2Near->pLevel(d2Ver)->dwLevelNo(d2Ver); if (currLevelNo == nearLevelNo) { continue; } auto nearPosX = pRoom2Near->dwPosX(d2Ver); auto nearPosY = pRoom2Near->dwPosY(d2Ver); auto nearSizeX = pRoom2Near->dwSizeX(d2Ver); auto nearSizeY = pRoom2Near->dwSizeY(d2Ver); auto roomSizeX = pRoom2->dwSizeX(d2Ver); auto roomSizeY = pRoom2->dwSizeY(d2Ver); if (nearPosX + nearSizeX == roomPosX && nearPosY == roomPosY) { side = 0; } else if (nearPosX == roomPosX + roomSizeX && nearPosY == roomPosY) { side = 1; } else if (nearPosY + nearSizeY == roomPosY && nearPosX == roomPosX) { side = 2; } else if (nearPosY == roomPosY + roomSizeY && nearPosX == roomPosX) { side = 3; } if (side < 0) { continue; } bool bAddedNear = false; if (!pRoom2Near->pRoom1(d2Ver)) { D2COMMON_AddRoomData(act, nearLevelNo, nearPosX, nearPosY, nullptr); bAddedNear = true; } int sideStart = -1; Room1 *room1; CollMap *coll; if ((room1 = pRoom2Near->pRoom1(d2Ver)) && (coll = room1->Coll(d2Ver))) { uint16_t *p = coll->pMapStart; auto w = coll->dwSizeGameX, h = coll->dwSizeGameY; switch (side) { case 0: p += w - 1; for (int z = 0; z < h; z++) { if ((*p & 1) || (*(p-1) & 1)) { if (sideStart >= 0) { sides.emplace_back(nearLevelNo, side, coll->dwPosGameY + sideStart, coll->dwPosGameY + z); sideStart = -1; } } else { if (sideStart < 0) { sideStart = z; } } p += w; } break; case 1: for (int z = 0; z < h; z++) { if ((*p & 1) || (*(p+1) & 1)) { if (sideStart >= 0) { sides.emplace_back(nearLevelNo, side, coll->dwPosGameY + sideStart, coll->dwPosGameY + z); sideStart = -1; } } else { if (sideStart < 0) { sideStart = z; } } p += w; } break; case 2: p += w * (h - 1); for (int z = 0; z < w; z++) { if ((*p & 1) || (*(p-w) & 1)) { if (sideStart >= 0) { sides.emplace_back(nearLevelNo, side, coll->dwPosGameX + sideStart, coll->dwPosGameX + z); sideStart = -1; } } else { if (sideStart < 0) { sideStart = z; } } p++; } break; case 3: for (int z = 0; z < w; z++) { if ((*p & 1) || (*(p+w) & 1)) { if (sideStart >= 0) { sides.emplace_back(nearLevelNo, side, coll->dwPosGameX + sideStart, coll->dwPosGameX + z); sideStart = -1; } } else { if (sideStart < 0) { sideStart = z; } } p++; } break; default: break; } if (sideStart >= 0) { if (side == 2 || side == 3) { sides.emplace_back(nearLevelNo, side, coll->dwPosGameX + sideStart, coll->dwPosGameX + w); } else { sides.emplace_back(nearLevelNo, side, coll->dwPosGameY + sideStart, coll->dwPosGameY + h); } } } if (bAddedNear) { D2COMMON_RemoveRoomData(act, nearLevelNo, nearPosX, nearPosY, nullptr); } } // add collision data Room1 *room1; CollMap *coll; if ((room1 = pRoom2->pRoom1(d2Ver)) && (coll = room1->Coll(d2Ver))) { const int x = coll->dwPosGameX - offset.x; const int y = coll->dwPosGameY - offset.y; const int cx = coll->dwSizeGameX; const int cy = coll->dwSizeGameY; const int nLimitX = x + cx; const int nLimitY = y + cy; uint16_t *p = coll->pMapStart; if (crop.x0 < 0 || x < crop.x0) crop.x0 = x; if (crop.y0 < 0 || y < crop.y0) crop.y0 = y; if (crop.x1 < 0 || nLimitX > crop.x1) crop.x1 = nLimitX; if (crop.y1 < 0 || nLimitY > crop.y1) crop.y1 = nLimitY; for (int j = y; j < nLimitY; j++) { int index = j * width + x; for (int i = x; i < nLimitX; i++) { map[index++] = *p++; } } } // add unit data for (PresetUnit *pPresetUnit = pRoom2->pPreset(d2Ver); pPresetUnit; pPresetUnit = pPresetUnit->pPresetNext(d2Ver)) { // npcs auto type = pPresetUnit->dwType(d2Ver); if (type == unit_type_npc) { const auto npcX = static_cast(roomPosX * 5 + pPresetUnit->dwPosX(d2Ver)); const auto npcY = static_cast(roomPosY * 5 + pPresetUnit->dwPosY(d2Ver)); npcs[pPresetUnit->dwTxtFileNo(d2Ver)].push_back(Point{npcX, npcY}); } // objects if (type == unit_type_object) { const auto objectX = static_cast(roomPosX * 5 + pPresetUnit->dwPosX(d2Ver)); const auto objectY = static_cast(roomPosY * 5 + pPresetUnit->dwPosY(d2Ver)); objects[pPresetUnit->dwTxtFileNo(d2Ver)].push_back(Point{objectX, objectY}); } // level exits if (type == unit_type_tile) { auto txtFileNo = pPresetUnit->dwTxtFileNo(d2Ver); auto presetPosX = pPresetUnit->dwPosX(d2Ver); auto presetPosY = pPresetUnit->dwPosY(d2Ver); for (RoomTile *pRoomTile = pRoom2->pRoomTiles(d2Ver); pRoomTile; pRoomTile = pRoomTile->pNext(d2Ver)) { if (*pRoomTile->nNum(d2Ver) == txtFileNo) { const auto exitX = static_cast(roomPosX * 5 + presetPosX); const auto exitY = static_cast(roomPosY * 5 + presetPosY); auto &al = exits[pRoomTile->pRoom2(d2Ver)->pLevel(d2Ver)->dwLevelNo(d2Ver)]; al.isPortal = true; al.offsets.emplace_back(Point{exitX, exitY}); } } } } if (bAdded) { D2COMMON_RemoveRoomData(act, currLevelNo, roomPosX, roomPosY, nullptr); } } std::sort(sides.begin(), sides.end()); std::vector> realSides; uint32_t lastNearLevelNo = 0; int lastSide = -1, start = -1, end = -1; for (auto [nearLevelNo, side, sideStart, sideEnd]: sides) { if (lastNearLevelNo != nearLevelNo || lastSide != side) { if (start >= 0) { realSides.emplace_back(lastNearLevelNo, lastSide, start, end); } lastSide = side; lastNearLevelNo = nearLevelNo; start = -1; end = -1; } if (start == -1) { start = sideStart; end = sideEnd; } else if (sideStart == end) { end = sideEnd; } else { realSides.emplace_back(lastNearLevelNo, lastSide, start, end); start = sideStart; end = sideEnd; } } if (start >= 0) { realSides.emplace_back(lastNearLevelNo, lastSide, start, end); } sides.clear(); for (auto [nearLevelNo, side, sideStart, sideEnd]: realSides) { int sStart = -1; switch (side) { case 0: { sideStart -= offset.y; if (sideStart < crop.y0) sideStart = crop.y0; sideEnd -= offset.y; if (sideEnd > crop.y1) sideEnd = crop.y1; if (sideStart == sideEnd) break; auto w = crop.x1 - crop.x0; auto *p = map.data() + (sideStart - crop.y0) * w; for (auto z = sideStart; z < sideEnd; ++z) { if ((*p & 1) || (*(p+1) & 1)) { if (sStart >= 0) { sides.emplace_back(nearLevelNo, side, sStart, z); sStart = -1; } } else { if (sStart < 0) { sStart = z; } } p += w; } break; } case 1: { sideStart -= offset.y; if (sideStart < crop.y0) sideStart = crop.y0; sideEnd -= offset.y; if (sideEnd > crop.y1) sideEnd = crop.y1; if (sideStart == sideEnd) break; auto w = crop.x1 - crop.x0; auto *p = map.data() + (sideStart - crop.y0) * w + (w - 1); for (auto z = sideStart; z < sideEnd; ++z) { if ((*p & 1) || (*(p-1) & 1)) { if (sStart >= 0) { sides.emplace_back(nearLevelNo, side, sStart, z); sStart = -1; } } else { if (sStart < 0) { sStart = z; } } p += w; } break; } case 2: { sideStart -= offset.x; if (sideStart < crop.x0) sideStart = crop.x0; sideEnd -= offset.x; if (sideEnd > crop.x1) sideEnd = crop.x1; if (sideStart == sideEnd) break; auto w = crop.x1 - crop.x0; auto *p = map.data() + (sideStart - crop.x0); for (auto z = sideStart; z < sideEnd; ++z) { if ((*p & 1) || (*(p+w) & 1)) { if (sStart >= 0) { sides.emplace_back(nearLevelNo, side, sStart, z); sStart = -1; } } else { if (sStart < 0) { sStart = z; } } p++; } break; } case 3: { sideStart -= offset.x; if (sideStart < crop.x0) sideStart = crop.x0; sideEnd -= offset.x; if (sideEnd > crop.x1) sideEnd = crop.x1; if (sideStart == sideEnd) break; auto w = crop.x1 - crop.x0; auto *p = map.data() + w * (crop.y1 - crop.y0 - 1) + (sideStart - crop.x0); for (auto z = sideStart; z < sideEnd; ++z) { if ((*p & 1) || (*(p-w) & 1)) { if (sStart >= 0) { sides.emplace_back(nearLevelNo, side, sStart, z); sStart = -1; } } else { if (sStart < 0) { sStart = z; } } p++; } break; } default: break; } if (sStart >= 0) { sides.emplace_back(nearLevelNo, side, sStart, sideEnd); } } for (auto [nearLevelNo, side, sideStart, sideEnd]: sides) { if (sideStart + 2 >= sideEnd) { continue; } switch (side) { case 0: exits[nearLevelNo].offsets.emplace_back(Point{offset.x + crop.x0, offset.y + (sideStart + sideEnd) / 2}); break; case 1: exits[nearLevelNo].offsets.emplace_back(Point{offset.x + crop.x1 - 1, offset.y + (sideStart + sideEnd) / 2}); break; case 2: exits[nearLevelNo].offsets.emplace_back(Point{offset.x + (sideStart + sideEnd) / 2, offset.y + crop.y0}); break; case 3: exits[nearLevelNo].offsets.emplace_back(Point{offset.x + (sideStart + sideEnd) / 2, offset.y + crop.y1 - 1}); break; default: break; } } /* run length encoding map data */ mapData.clear(); for (int j = crop.y0; j < crop.y1; ++j) { int index = j * width + crop.x0; bool lastIsWalkable = false; int count = 0; for (int i = crop.x0; i < crop.x1; ++i) { bool walkable = !(map[index++] & 1); if (walkable == lastIsWalkable) { ++count; continue; } mapData.emplace_back(count); count = 1; lastIsWalkable = walkable; } mapData.emplace_back(count); mapData.emplace_back(-1); } /* generate path data */ if (generatePathData) { genPathData(map.data()); } else { pathData.clear(); } } void MapData::genPathData(const int16_t *map) { pathData.clear(); auto w = (crop.x1 - crop.x0) / 5; auto h = (crop.y1 - crop.y0) / 5; path.resize(w * h, 0); int pathIndex = 0; auto width = size.width; for (int j = crop.y0; j < crop.y1; j += 5) { int index = j * width + crop.x0; for (int i = crop.x0; i < crop.x1; i += 5, index += 5, pathIndex++) { if (i + 5 < crop.x1) { for (int p = 1; p < 4; ++p) { for (int q = 3; q < 7; ++q) { if (map[index + p * width + q] & 1) { goto out; } } path[pathIndex] |= 2; path[pathIndex + 1] |= 1; break; out:; } } if (j + 5 < crop.y1) { for (int p = 1; p < 4; ++p) { for (int q = 3; q < 7; ++q) { if (map[index + q * width + p] & 1) { goto out2; } } path[pathIndex] |= 8; path[pathIndex + w] |= 4; break; out2:; } } } } /* run length encoding path data */ pathData.clear(); uint32_t count = 0; uint8_t lastByte = path[0]; for (auto v: path) { if (v != lastByte) { while (count > 255) { pathData.emplace_back(lastByte); pathData.emplace_back(255); count -= 255; } pathData.emplace_back(lastByte); pathData.emplace_back(count); lastByte = v; count = 1; } else { ++count; } } while (count > 255) { pathData.emplace_back(lastByte); pathData.emplace_back(255); count -= 255; } pathData.emplace_back(lastByte); pathData.emplace_back(count); } } ================================================ FILE: d2mapapi/mapdata.h ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #pragma once #include "collisionmap.h" #include "d2structs.h" namespace d2mapapi { class MapData: public CollisionMap { public: MapData(Act *act, unsigned int areaId, bool generatePathData = false); void genPathData(const int16_t *map); }; } ================================================ FILE: d2mapapi/offset.cpp ================================================ #include "offset.h" #define _DEFINE_VARS #include "d2ptrs.h" #include "crc32.h" #include #include #include #include namespace d2mapapi { static D2Version d2version = D2_113c; D2Version getD2Version() { return d2version; } bool defineOffsets() { const struct DllOffset { const char *dll; void *data; int32_t ordinal; } offsets_111a[] = { {"STORM.DLL", &p_STORM_MPQHashTable, 0x52B00}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_1, 0x5AF10}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_2, 0x5ABD0}, {"D2CLIENT.DLL", &D2CLIENT_InitGameMisc_I, 0x8901B}, {"D2COMMON.DLL", &D2COMMON_AddRoomData, -10432}, {"D2COMMON.DLL", &D2COMMON_RemoveRoomData, -10716}, {"D2COMMON.DLL", &D2COMMON_GetLevel, -10204}, {"D2COMMON.DLL", &D2COMMON_InitLevel, -10972}, {"D2COMMON.DLL", &D2COMMON_LoadAct, 0x137F0}, /* -10141 */ {"D2COMMON.DLL", &D2COMMON_UnloadAct, -10155}, {"FOG.DLL", &FOG_10021, -10021}, {"FOG.DLL", &FOG_10101, -10101}, {"FOG.DLL", &FOG_10089, -10089}, {"FOG.DLL", &FOG_10218, -10218}, {"D2WIN.DLL", &D2WIN_10086, -10176}, {"D2WIN.DLL", &D2WIN_10005, -10063}, {"D2LANG.DLL", &D2LANG_Init, -10001}, {"D2COMMON.DLL", &D2COMMON_InitDataTables, -10149}, {nullptr}, }, offsets_111b[] = { {"STORM.DLL", &p_STORM_MPQHashTable, 0x54D30}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_1, 0x52F40}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_2, 0x52C00}, {"D2CLIENT.DLL", &D2CLIENT_InitGameMisc_I, 0x32E5B}, {"D2COMMON.DLL", &D2COMMON_AddRoomData, -10787}, {"D2COMMON.DLL", &D2COMMON_RemoveRoomData, -10672}, {"D2COMMON.DLL", &D2COMMON_GetLevel, -11058}, {"D2COMMON.DLL", &D2COMMON_InitLevel, -10741}, {"D2COMMON.DLL", &D2COMMON_LoadAct, 0x264A0}, /* -10669 */ {"D2COMMON.DLL", &D2COMMON_UnloadAct, -10651}, {"FOG.DLL", &FOG_10021, -10021}, {"FOG.DLL", &FOG_10101, -10101}, {"FOG.DLL", &FOG_10089, -10089}, {"FOG.DLL", &FOG_10218, -10218}, {"D2WIN.DLL", &D2WIN_10086, -10030}, {"D2WIN.DLL", &D2WIN_10005, -10051}, {"D2LANG.DLL", &D2LANG_Init, -10009}, {"D2COMMON.DLL", &D2COMMON_InitDataTables, -10506}, {nullptr}, }, offsets_112a[] = { {"STORM.DLL", &p_STORM_MPQHashTable, 0x55358}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_1, 0x409E0}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_2, 0x406A0}, {"D2CLIENT.DLL", &D2CLIENT_InitGameMisc_I, 0x7CD2B}, {"D2COMMON.DLL", &D2COMMON_AddRoomData, -10184}, {"D2COMMON.DLL", &D2COMMON_RemoveRoomData, -11009}, {"D2COMMON.DLL", &D2COMMON_GetLevel, -11020}, {"D2COMMON.DLL", &D2COMMON_InitLevel, -10721}, {"D2COMMON.DLL", &D2COMMON_LoadAct, 0x56780}, /* -10588 */ {"D2COMMON.DLL", &D2COMMON_UnloadAct, -10710}, {"FOG.DLL", &FOG_10021, -10021}, {"FOG.DLL", &FOG_10101, -10101}, {"FOG.DLL", &FOG_10089, -10089}, {"FOG.DLL", &FOG_10218, -10218}, {"D2WIN.DLL", &D2WIN_10086, -10059}, {"D2WIN.DLL", &D2WIN_10005, -10073}, {"D2LANG.DLL", &D2LANG_Init, -10003}, {"D2COMMON.DLL", &D2COMMON_InitDataTables, -10797}, {nullptr}, }, offsets_113c[] = { {"STORM.DLL", &p_STORM_MPQHashTable, 0x53120}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_1, 0x62AA0}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_2, 0x62760}, {"D2CLIENT.DLL", &D2CLIENT_InitGameMisc_I, 0x4454B}, {"D2COMMON.DLL", &D2COMMON_AddRoomData, -10401}, {"D2COMMON.DLL", &D2COMMON_RemoveRoomData, -11099}, {"D2COMMON.DLL", &D2COMMON_GetLevel, -10207}, {"D2COMMON.DLL", &D2COMMON_InitLevel, -10322}, {"D2COMMON.DLL", &D2COMMON_LoadAct, 0x3CB30}, {"D2COMMON.DLL", &D2COMMON_UnloadAct, -10868}, {"FOG.DLL", &FOG_10021, -10021}, {"FOG.DLL", &FOG_10101, -10101}, {"FOG.DLL", &FOG_10089, -10089}, {"FOG.DLL", &FOG_10218, -10218}, {"D2WIN.DLL", &D2WIN_10086, -10086}, {"D2WIN.DLL", &D2WIN_10005, -10005}, {"D2LANG.DLL", &D2LANG_Init, -10008}, {"D2COMMON.DLL", &D2COMMON_InitDataTables, -10943}, {nullptr}, }, offsets_113d[] = { {"STORM.DLL", &p_STORM_MPQHashTable, 0x52A60}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_1, 0x737F0}, {"D2CLIENT.DLL", &D2CLIENT_LoadAct_2, 0x2B420}, {"D2CLIENT.DLL", &D2CLIENT_InitGameMisc_I, 0x4559B}, {"D2COMMON.DLL", &D2COMMON_AddRoomData, 0x24990}, {"D2COMMON.DLL", &D2COMMON_RemoveRoomData, 0x24930}, {"D2COMMON.DLL", &D2COMMON_GetLevel, 0x6D440}, {"D2COMMON.DLL", &D2COMMON_InitLevel, 0x6DDF0}, {"D2COMMON.DLL", &D2COMMON_LoadAct, 0x24810}, {"D2COMMON.DLL", &D2COMMON_UnloadAct, 0x24590}, {"FOG.DLL", &FOG_10021, -10021}, {"FOG.DLL", &FOG_10101, -10101}, {"FOG.DLL", &FOG_10089, -10089}, {"FOG.DLL", &FOG_10218, -10218}, {"D2WIN.DLL", &D2WIN_10086, -10174}, {"D2WIN.DLL", &D2WIN_10005, -10072}, {"D2LANG.DLL", &D2LANG_Init, -10009}, {"D2COMMON.DLL", &D2COMMON_InitDataTables, -10081}, {nullptr}, }, *offsets = nullptr; const struct DllSizeToVersion { uint32_t gameCrc32; uint32_t stormCrc32; const DllOffset *offsets; D2Version version; } sizeMap[] = { { 0xf44cd0cf, 0x9f06891d, offsets_111a, D2_111a }, { 0x8fd3f392, 0xb6390775, offsets_111b, D2_111b }, { 0xab566eaa, 0xe5b0f351, offsets_112a, D2_112a }, { 0xea2f0e6e, 0x5711a8b4, offsets_113c, D2_113c }, { 0xb3d69c47, 0xbdb6784e, offsets_113d, D2_113d }, }; uint32_t gameCrc32, stormCrc32; auto crc32File = [](const wchar_t *filename)->uint32_t { auto file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (file == INVALID_HANDLE_VALUE) { return 0; } std::string data; auto size = GetFileSize(file, nullptr); data.resize(size); DWORD bytesRead; ReadFile(file, data.data(), size, &bytesRead, nullptr); CloseHandle(file); return crc::crc32(data.data(), size); }; /* Do CRC check for game.exe first, fallback to Storm.dll */ gameCrc32 = crc32File(L"Game.exe"); stormCrc32 = gameCrc32 ? 0 : crc32File(L"Storm.dll"); for (auto &sm: sizeMap) { if (gameCrc32 == sm.gameCrc32 || stormCrc32 == sm.stormCrc32) { offsets = sm.offsets; d2version = sm.version; break; } } if (!offsets) { return false; } for (const auto *off = offsets; off->dll; off++) { HMODULE hMod = GetModuleHandle(off->dll); if (!hMod) { hMod = LoadLibrary(off->dll); } if (!hMod) { return false; } uintptr_t addr; if (off->ordinal < 0) { addr = (uintptr_t)GetProcAddress(hMod, (const char *)-off->ordinal); } else { addr = (uintptr_t)hMod + off->ordinal; } if (!addr) { return false; } *(uintptr_t*)off->data = addr; } return true; } } ================================================ FILE: d2mapapi/offset.h ================================================ #pragma once #include namespace d2mapapi { enum D2Version { D2_111a, D2_111b, D2_112a, D2_113c, D2_113d, }; D2Version getD2Version(); bool defineOffsets(); } ================================================ FILE: d2mapapi/pathfinder.cpp ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #include "pathfinder.h" #include #include namespace d2mapapi { std::vector> pathFindBFS(int startX, int startY, int targetX, int targetY, const uint8_t *mapData, int mapWidth, int mapHeight, bool merge) { const int n = mapWidth * mapHeight; if (startX >= mapWidth || startY >= mapHeight || targetX >= mapWidth || targetY >= mapHeight) { return {}; } const int startPos = startX + startY * mapWidth, targetPos = targetX + targetY * mapWidth; std::vector> p(n, {-1, -1}); p[startPos].second = 0; std::queue q; q.push(startPos); int offset[] = {0, -1, +1, 0, -mapWidth, 0, 0, 0, +mapWidth}; while (!q.empty()) { int u = q.front(); q.pop(); for (auto e: {1, 2, 4, 8}) { if (!(mapData[u] & e)) { continue; } int off = offset[e]; int v = u + off; if ((off == 1 && (v % mapWidth == 0)) || (off == -1 && (u % mapWidth == 0))) continue; if (0 <= v && v < n && p[v].second == -1 && mapData[v]) { p[v] = { u, p[u].second + 1 }; if (v == targetPos) goto end; q.push(v); } } } end: if (p[targetPos].second == -1) { return {}; } std::vector> result; result.reserve(p[targetPos].second); if (merge) { int curr = targetPos; if (curr == -1) { return {}; } int prevX = curr % mapWidth, prevY = curr / mapWidth; int lastX = prevX, lastY = prevY; result.emplace_back(lastX, lastY); curr = p[curr].first; while (curr != -1) { int currX = curr % mapWidth, currY = curr / mapWidth; bool blocked = false; /* Check if line is blocked here */ if (currX == prevX) { int delta = prevY < currY ? 1 : -1; int indexDelta = delta * mapWidth; int checkBit = delta == 1 ? 8 : 4; int index = prevY * mapWidth + prevX; for (int y = prevY; y != currY; y += delta, index += indexDelta) { if (!(mapData[index] & checkBit)) { blocked = true; break; } } } else if (currY == prevY) { int delta = prevX < currX ? 1 : -1; int checkBit = delta == 1 ? 2 : 1; int index = prevY * mapWidth + prevX; for (int x = prevX; x != currX; x += delta, index += delta) { if (!(mapData[index] & checkBit)) { blocked = true; break; } } } else { int dx = std::abs(currX - prevX); int dy = std::abs(currY - prevY); if (dx < dy) { int delta = prevY < currY ? 1 : -1; int indexDelta = delta * mapWidth; int delta2 = prevX < currX ? 1 : -1; int checkBit = delta == 1 ? 8 : 4; int checkBit2 = delta2 == 1 ? 2 : 1; int index = prevY * mapWidth + prevX; int total = dy / 2; for (int y = prevY; y != currY; y += delta, index += indexDelta) { total += dx; if (total >= dy) { total -= dy; auto val0 = dx - total; if (val0 >= total) { if (!(mapData[index] & checkBit) || !(mapData[index + indexDelta] & checkBit2)) { blocked = true; break; } } else { if (!(mapData[index] & checkBit2) || !(mapData[index + delta2] & checkBit)) { blocked = true; break; } } index += delta2; } else { if (!(mapData[index] & checkBit)) { blocked = true; break; } } } } else { int delta = prevX < currX ? 1 : -1; int delta2 = prevY < currY ? 1 : -1; int indexDelta2 = delta2 * mapWidth; int checkBit = delta == 1 ? 2 : 1; int checkBit2 = delta2 == 1 ? 8 : 4; int index = prevY * mapWidth + prevX; int total = dx / 2; for (int x = prevX; x != currX; x += delta, index += delta) { total += dy; if (total >= dx) { total -= dx; auto val0 = dy - total; if (val0 >= total) { if (!(mapData[index] & checkBit) || !(mapData[index + delta] & checkBit2)) { blocked = true; break; } } else { if (!(mapData[index] & checkBit2) || !(mapData[index + indexDelta2] & checkBit)) { blocked = true; break; } } index += indexDelta2; } else { if (!(mapData[index] & checkBit)) { blocked = true; break; } } } } } if (blocked) { result.emplace_back(lastX, lastY); prevX = lastX; prevY = lastY; continue; } lastX = currX; lastY = currY; curr = p[curr].first; } result.emplace_back(lastX, lastY); return result; } int curr = targetPos; while (curr != -1) { int currX = curr % mapWidth, currY = curr / mapWidth; result.emplace_back(currX, currY); curr = p[curr].first; } return std::move(result); } } ================================================ FILE: d2mapapi/pathfinder.h ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #pragma once #include #include #include namespace d2mapapi { std::vector> pathFindBFS(int startX, int startY, int targetX, int targetY, const uint8_t *mapData, int mapWidth, int mapHeight, bool merge = false); } ================================================ FILE: d2mapapi/piped.cpp ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #include "d2map.h" #include "session.h" #include #include #include #include #include enum { SessionsCacheSize = 8, }; int wmain(int argc, wchar_t *argv[]) { HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); if ((hStdout == INVALID_HANDLE_VALUE) || (hStdin == INVALID_HANDLE_VALUE)) { ExitProcess(1); } int ret = 0; const auto *errstr = argc > 1 ? d2mapapi::d2Init(argv[1]) : "Usage: d2mapapi_piped "; if (errstr) { do { HKEY key; if (RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Blizzard Entertainment\\Diablo II", 0, KEY_READ, &key) == ERROR_SUCCESS) { wchar_t path[MAX_PATH]; DWORD pathSize = sizeof(path); if (RegQueryValueExW(key, L"InstallPath", nullptr, nullptr, LPBYTE(path), &pathSize) == ERROR_SUCCESS) { errstr = d2mapapi::d2Init(path); if (!errstr) { RegCloseKey(key); break; } } RegCloseKey(key); } ret = -1; DWORD written; WriteFile(hStdout, &ret, sizeof(int), &written, nullptr); nlohmann::json j; j["error"] = std::string("[d2mapapi_mod v" D2MAPAPI_VERSION "] ") + errstr; auto str = j.dump(); auto sz = uint32_t(str.size()); WriteFile(hStdout, &sz, sizeof(uint32_t), &written, nullptr); WriteFile(hStdout, str.c_str(), sz, &written, nullptr); return -1; } while (false); } DWORD written; WriteFile(hStdout, &ret, sizeof(int), &written, nullptr); std::unordered_map> sessions; std::vector sessionsOrder; for (;;) { struct Req { uint32_t seed; uint32_t difficulty; uint32_t levelId; }; Req req = {}; DWORD bytesRead; if (!ReadFile(hStdin, &req, sizeof(uint32_t) * 3, &bytesRead, nullptr)) { break; } auto key = uint64_t(req.seed) | (uint64_t(req.difficulty) << 32); auto &session = sessions[key]; if (!session) { sessionsOrder.emplace_back(key); session = std::make_unique(); session->update(req.seed, req.difficulty); if (sessionsOrder.size() > SessionsCacheSize) { auto oldKey = sessionsOrder[0]; sessionsOrder.erase(sessionsOrder.begin()); sessions.erase(oldKey); } } auto levelId = req.levelId & 0xFFFFu; auto option = req.levelId >> 16; const auto *map = session->getMap(levelId, (option & 1u) != 0); std::string str; if (map) { str = map->encode((option & 1u) != 0); } else { str = R"({"error":"[d2mapapi_mod v)" D2MAPAPI_VERSION R"(] Invalid map id!"})"; } auto sz = uint32_t(str.size()); if (!WriteFile(hStdout, &sz, sizeof(uint32_t), &written, nullptr) || !WriteFile(hStdout, str.c_str(), sz, &written, nullptr)) { break; } } return 0; } ================================================ FILE: d2mapapi/pipehost.cpp ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #include "pipehost.h" #include #include #include #include namespace d2mapapi { PipedChildProcess::~PipedChildProcess() { if (process) { CloseHandle(HANDLE(process)); CloseHandle(HANDLE(childStdoutRd)); CloseHandle(HANDLE(childStdinWr)); } } bool PipedChildProcess::start(const wchar_t *filename, const wchar_t *parameters) { SECURITY_ATTRIBUTES saAttr = { .nLength = sizeof(SECURITY_ATTRIBUTES), .lpSecurityDescriptor = nullptr, .bInheritHandle = TRUE, }; if (!CreatePipe((HANDLE*)&childStdoutRd, (HANDLE*)&childStdoutWr, &saAttr, 0) || !CreatePipe((HANDLE*)&childStdinRd, (HANDLE*)&childStdinWr, &saAttr, 0)) { return false; } SetHandleInformation(HANDLE(childStdoutRd), HANDLE_FLAG_INHERIT, 0); SetHandleInformation(HANDLE(childStdinWr), HANDLE_FLAG_INHERIT, 0); PROCESS_INFORMATION piProcInfo = {}; STARTUPINFOW siStartInfo = {.cb = sizeof(STARTUPINFOW), .dwFlags = STARTF_USESTDHANDLES, .hStdInput = childStdinRd, .hStdOutput = childStdoutWr, .hStdError = childStdoutWr}; BOOL bSuccess = FALSE; wchar_t cmdLine[1024]; wsprintfW(cmdLine, L"\"%s\" \"%s\"", filename, parameters); bSuccess = CreateProcessW(nullptr, cmdLine, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo); process = piProcInfo.hProcess; if (!bSuccess) { return false; } CloseHandle(piProcInfo.hThread); CloseHandle(HANDLE(childStdoutWr)); CloseHandle(HANDLE(childStdinRd)); int ret; if (!readPipe(&ret, 4)) { errMsg_ = "failed to read from child process!"; return false; } if (ret == 0) { return true; } uint32_t len; readPipe(&len, 4); std::string str; str.resize(len); readPipe(str.data(), len); try { auto j = nlohmann::json::parse(str); errMsg_ = j["error"]; } catch(const std::exception &e) { errMsg_ = e.what(); } return false; } bool PipedChildProcess::writePipe(const void *data, size_t size) { DWORD dwWritten; return WriteFile(HANDLE(childStdinWr), data, DWORD(size), &dwWritten, nullptr); } bool PipedChildProcess::readPipe(void *data, size_t size) { DWORD dwRead; return ReadFile(HANDLE(childStdoutRd), data, DWORD(size), &dwRead, nullptr); } std::string PipedChildProcess::queryMapRaw(uint32_t seed, uint8_t difficulty, uint32_t levelId, bool generatePathData) { struct Req { uint32_t seed; uint32_t difficulty; uint32_t levelId; }; Req req = {.seed = seed, .difficulty = difficulty, .levelId = levelId | ((generatePathData ? 1u : 0u) << 16)}; uint32_t size; if (!writePipe(&req, sizeof(uint32_t) * 3)) { return ""; } if (!readPipe(&size, sizeof(size))) { return ""; } std::string str; str.resize(size); if (!readPipe(str.data(), str.length())) { return ""; } return std::move(str); } CollisionMap *PipedChildProcess::queryMap(uint32_t seed, uint8_t difficulty, uint32_t levelId, bool generatePathData) { auto str = queryMapRaw(seed, difficulty, levelId, generatePathData); if (str.empty()) { return nullptr; } try { return new CollisionMap(str); } catch(...) { return nullptr; } } } ================================================ FILE: d2mapapi/pipehost.h ================================================ /* * Copyright (c) 2021 Soar Qin * * Use of this source code is governed by an MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ #pragma once #include "collisionmap.h" #include #include namespace d2mapapi { class PipedChildProcess final { public: ~PipedChildProcess(); bool start(const wchar_t *filename, const wchar_t *parameters); bool writePipe(const void *data, size_t size); bool readPipe(void *data, size_t size); [[nodiscard]] inline const std::string &errMsg() const { return errMsg_; } std::string queryMapRaw(uint32_t seed, uint8_t difficulty, uint32_t levelId, bool generatePathData = false); CollisionMap *queryMap(uint32_t seed, uint8_t difficulty, uint32_t levelId, bool generatePathData = false); private: void *childStdinRd = nullptr; void *childStdinWr = nullptr; void *childStdoutRd = nullptr; void *childStdoutWr = nullptr; void *process = nullptr; std::string errMsg_; }; } ================================================ FILE: d2mapapi/session.cpp ================================================ #include "session.h" #include "d2ptrs.h" namespace d2mapapi { static const unsigned int ActLevels[] = {1, 40, 75, 103, 109, 137}; namespace Helpers { int getAct(unsigned int areaid) { for (int i = 0; i < 6; i++) { if (areaid < ActLevels[i]) { return i - 1; } } return -1; } } Session::~Session() { unloadAll(); } bool Session::update(unsigned int seed, unsigned char difficulty) { if (difficulty > 2) difficulty = 2; if (seed_ == seed && difficulty == difficulty_) { return false; } seed_ = seed; difficulty_ = difficulty; unloadAll(); return true; } const CollisionMap *Session::getMap(unsigned int areaid, bool generatePathData) { auto ite = maps_.find(areaid); if (ite != maps_.end()) { if (generatePathData && ite->second->pathData.empty()) { auto w = ite->second->size.width; auto h = ite->second->size.height; std::vector map(w, h); if (ite->second->extractCellData(map.data(), w, h, 0, 0, 1, 0)) { ite->second->genPathData(map.data()); } } return ite->second.get(); } auto actId = Helpers::getAct(areaid); if (actId < 0) { return nullptr; } if (!acts_[actId]) { acts_[actId] = D2COMMON_LoadAct(actId, seed_, 1 /*TRUE*/, nullptr, difficulty_, nullptr, ActLevels[actId], D2CLIENT_LoadAct_1, D2CLIENT_LoadAct_2); } auto map = std::make_unique(acts_[actId], areaid, generatePathData); if (!map->built) { return nullptr; } auto &output = maps_[areaid]; output = std::move(map); return output.get(); } void Session::unloadAll() { maps_.clear(); for (auto *&act: acts_) { if (act) { D2COMMON_UnloadAct(act); act = nullptr; } } } } ================================================ FILE: d2mapapi/session.h ================================================ #pragma once #include "mapdata.h" #include #include namespace d2mapapi { class Session { public: ~Session(); bool update(unsigned int seed, unsigned char difficulty); const CollisionMap *getMap(unsigned int areaid, bool generatePathData = false); private: void unloadAll(); private: std::map> maps_; unsigned int seed_ = 0; unsigned char difficulty_ = 0; Act *acts_[5] = {}; }; } ================================================ FILE: d2mapapi/simphttp/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13) project(simphttp) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") find_package(Git QUIET REQUIRED) include(ExternalProject) find_package(LibUV QUIET) if(LIBUV_FOUND) message(STATUS "Using system libuv") add_library(libuv_external INTERFACE) target_include_directories(libuv_external INTERFACE ${LIBUV_INCLUDE_DIRS}) target_link_libraries(libuv_external INTERFACE ${LIBUV_LIBRARIES}) add_library(uv::libuv ALIAS libuv_external) else() ExternalProject_Add(libuv PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libuv STEP_TARGETS update GIT_REPOSITORY https://github.com/libuv/libuv.git GIT_TAG v1.x GIT_SHALLOW ON UPDATE_DISCONNECTED ON STEP_TARGETS update CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_LIBDIR=lib -DLIBUV_BUILD_TESTS=OFF -DLIBUV_BUILD_BENCH=OFF ) add_library(uv-static INTERFACE) add_dependencies(uv-static libuv) target_include_directories(uv-static INTERFACE ${CMAKE_BINARY_DIR}/include) target_link_directories(uv-static INTERFACE ${CMAKE_BINARY_DIR}/lib) find_package(Threads) target_link_libraries(uv-static INTERFACE uv_a Threads::Threads ${CMAKE_DL_LIBS}) if(WIN32) target_link_libraries(uv-static INTERFACE ws2_32 iphlpapi userenv) endif() add_library(uv::libuv ALIAS uv-static) endif() add_subdirectory(llhttp) add_library(${PROJECT_NAME} STATIC EXCLUDE_FROM_ALL simphttp.cpp simphttp.h) target_include_directories(${PROJECT_NAME} PUBLIC .) target_link_libraries(${PROJECT_NAME} llhttp uv::libuv) ================================================ FILE: d2mapapi/simphttp/cmake/FindLibUV.cmake ================================================ # - Try to find libuv # Once done, this will define # # LIBUV_FOUND - system has libuv # LIBUV_INCLUDE_DIRS - the libuv include directories # LIBUV_LIBRARIES - link these to use libuv if(UNIX) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_LIBUV QUIET libuv) endif() endif() find_path(LIBUV_INCLUDE_DIR uv.h HINTS ${PC_LIBUV_INCLUDEDIR} ${PC_LIBUV_INCLUDE_DIRS}) list(APPEND LIBUV_NAMES uv) find_library(LIBUV_LIBRARY NAMES ${LIBUV_NAMES} HINTS ${PC_LIBUV_LIBDIR} ${PC_LIBUV_LIBRARY_DIRS}) mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) if(PC_LIBUV_LIBRARIES) list(REMOVE_ITEM PC_LIBUV_LIBRARIES uv) endif() set(LIBUV_LIBRARIES ${LIBUV_LIBRARY} ${PC_LIBUV_LIBRARIES}) set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR}) # Deal with the fact that libuv.pc is missing important dependency information. include(CheckLibraryExists) check_library_exists(dl dlopen "dlfcn.h" HAVE_LIBDL) if(HAVE_LIBDL) list(APPEND LIBUV_LIBRARIES dl) endif() check_library_exists(kstat kstat_lookup "kstat.h" HAVE_LIBKSTAT) if(HAVE_LIBKSTAT) list(APPEND LIBUV_LIBRARIES kstat) endif() check_library_exists(kvm kvm_open "kvm.h" HAVE_LIBKVM) if(HAVE_LIBKVM AND NOT CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") list(APPEND LIBUV_LIBRARIES kvm) endif() check_library_exists(nsl gethostbyname "nsl.h" HAVE_LIBNSL) if(HAVE_LIBNSL) list(APPEND LIBUV_LIBRARIES nsl) endif() check_library_exists(perfstat perfstat_cpu "libperfstat.h" HAVE_LIBPERFSTAT) if(HAVE_LIBPERFSTAT) list(APPEND LIBUV_LIBRARIES perfstat) endif() check_library_exists(rt clock_gettime "time.h" HAVE_LIBRT) if(HAVE_LIBRT) list(APPEND LIBUV_LIBRARIES rt) endif() check_library_exists(sendfile sendfile "" HAVE_LIBSENDFILE) if(HAVE_LIBSENDFILE) list(APPEND LIBUV_LIBRARIES sendfile) endif() if(WIN32) # check_library_exists() does not work for Win32 API calls in X86 due to name # mangling calling conventions list(APPEND LIBUV_LIBRARIES iphlpapi) list(APPEND LIBUV_LIBRARIES psapi) list(APPEND LIBUV_LIBRARIES userenv) list(APPEND LIBUV_LIBRARIES ws2_32) endif() include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBUV_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(LibUV DEFAULT_MSG LIBUV_LIBRARY LIBUV_INCLUDE_DIR) mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) ================================================ FILE: d2mapapi/simphttp/llhttp/CMakeLists.txt ================================================ project(llhttp) add_library(${PROJECT_NAME} STATIC EXCLUDE_FROM_ALL api.c api.h http.c llhttp.c llhttp.h) target_include_directories(${PROJECT_NAME} PUBLIC .) ================================================ FILE: d2mapapi/simphttp/llhttp/api.c ================================================ #include #include #include #include "llhttp.h" #define CALLBACK_MAYBE(PARSER, NAME) \ do { \ const llhttp_settings_t* settings; \ settings = (const llhttp_settings_t*) (PARSER)->settings; \ if (settings == NULL || settings->NAME == NULL) { \ err = 0; \ break; \ } \ err = settings->NAME((PARSER)); \ } while (0) #define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \ do { \ const llhttp_settings_t* settings; \ settings = (const llhttp_settings_t*) (PARSER)->settings; \ if (settings == NULL || settings->NAME == NULL) { \ err = 0; \ break; \ } \ err = settings->NAME((PARSER), (START), (LEN)); \ if (err == -1) { \ err = HPE_USER; \ llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \ } \ } while (0) void llhttp_init(llhttp_t* parser, llhttp_type_t type, const llhttp_settings_t* settings) { llhttp__internal_init(parser); parser->type = type; parser->settings = (void*) settings; } #if defined(__wasm__) extern int wasm_on_message_begin(llhttp_t * p); extern int wasm_on_url(llhttp_t* p, const char* at, size_t length); extern int wasm_on_status(llhttp_t* p, const char* at, size_t length); extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length); extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length); extern int wasm_on_headers_complete(llhttp_t * p, int status_code, uint8_t upgrade, int should_keep_alive); extern int wasm_on_body(llhttp_t* p, const char* at, size_t length); extern int wasm_on_message_complete(llhttp_t * p); static int wasm_on_headers_complete_wrap(llhttp_t* p) { return wasm_on_headers_complete(p, p->status_code, p->upgrade, llhttp_should_keep_alive(p)); } const llhttp_settings_t wasm_settings = { wasm_on_message_begin, wasm_on_url, wasm_on_status, wasm_on_header_field, wasm_on_header_value, wasm_on_headers_complete_wrap, wasm_on_body, wasm_on_message_complete, NULL, NULL, }; llhttp_t* llhttp_alloc(llhttp_type_t type) { llhttp_t* parser = malloc(sizeof(llhttp_t)); llhttp_init(parser, type, &wasm_settings); return parser; } void llhttp_free(llhttp_t* parser) { free(parser); } /* Some getters required to get stuff from the parser */ uint8_t llhttp_get_type(llhttp_t* parser) { return parser->type; } uint8_t llhttp_get_http_major(llhttp_t* parser) { return parser->http_major; } uint8_t llhttp_get_http_minor(llhttp_t* parser) { return parser->http_minor; } uint8_t llhttp_get_method(llhttp_t* parser) { return parser->method; } int llhttp_get_status_code(llhttp_t* parser) { return parser->status_code; } uint8_t llhttp_get_upgrade(llhttp_t* parser) { return parser->upgrade; } #endif // defined(__wasm__) void llhttp_reset(llhttp_t* parser) { llhttp_type_t type = parser->type; const llhttp_settings_t* settings = parser->settings; void* data = parser->data; uint8_t lenient_flags = parser->lenient_flags; llhttp__internal_init(parser); parser->type = type; parser->settings = (void*) settings; parser->data = data; parser->lenient_flags = lenient_flags; } llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) { return llhttp__internal_execute(parser, data, data + len); } void llhttp_settings_init(llhttp_settings_t* settings) { memset(settings, 0, sizeof(*settings)); } llhttp_errno_t llhttp_finish(llhttp_t* parser) { int err; /* We're in an error state. Don't bother doing anything. */ if (parser->error != 0) { return 0; } switch (parser->finish) { case HTTP_FINISH_SAFE_WITH_CB: CALLBACK_MAYBE(parser, on_message_complete); if (err != HPE_OK) return err; /* FALLTHROUGH */ case HTTP_FINISH_SAFE: return HPE_OK; case HTTP_FINISH_UNSAFE: parser->reason = "Invalid EOF state"; return HPE_INVALID_EOF_STATE; default: abort(); } } void llhttp_pause(llhttp_t* parser) { if (parser->error != HPE_OK) { return; } parser->error = HPE_PAUSED; parser->reason = "Paused"; } void llhttp_resume(llhttp_t* parser) { if (parser->error != HPE_PAUSED) { return; } parser->error = 0; } void llhttp_resume_after_upgrade(llhttp_t* parser) { if (parser->error != HPE_PAUSED_UPGRADE) { return; } parser->error = 0; } llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) { return parser->error; } const char* llhttp_get_error_reason(const llhttp_t* parser) { return parser->reason; } void llhttp_set_error_reason(llhttp_t* parser, const char* reason) { parser->reason = reason; } const char* llhttp_get_error_pos(const llhttp_t* parser) { return parser->error_pos; } const char* llhttp_errno_name(llhttp_errno_t err) { #define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME; switch (err) { HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) default: abort(); } #undef HTTP_ERRNO_GEN } const char* llhttp_method_name(llhttp_method_t method) { #define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING; switch (method) { HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN) default: abort(); } #undef HTTP_METHOD_GEN } void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_HEADERS; } else { parser->lenient_flags &= ~LENIENT_HEADERS; } } void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_CHUNKED_LENGTH; } else { parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH; } } void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) { if (enabled) { parser->lenient_flags |= LENIENT_KEEP_ALIVE; } else { parser->lenient_flags &= ~LENIENT_KEEP_ALIVE; } } /* Callbacks */ int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_message_begin); return err; } int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p); return err; } int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_url_complete); return err; } int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p); return err; } int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_status_complete); return err; } int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p); return err; } int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_header_field_complete); return err; } int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p); return err; } int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_header_value_complete); return err; } int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_headers_complete); return err; } int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_message_complete); return err; } int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) { int err; SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p); return err; } int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_chunk_header); return err; } int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_chunk_complete); return err; } /* Private */ void llhttp__debug(llhttp_t* s, const char* p, const char* endp, const char* msg) { if (p == endp) { fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type, s->flags, msg); } else { fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s, s->type, s->flags, *p, msg); } } ================================================ FILE: d2mapapi/simphttp/llhttp/api.h ================================================ #ifndef INCLUDE_LLHTTP_API_H_ #define INCLUDE_LLHTTP_API_H_ #ifdef __cplusplus extern "C" { #endif #include #if defined(__wasm__) #define LLHTTP_EXPORT __attribute__((visibility("default"))) #else #define LLHTTP_EXPORT #endif typedef llhttp__internal_t llhttp_t; typedef struct llhttp_settings_s llhttp_settings_t; typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length); typedef int (*llhttp_cb)(llhttp_t*); struct llhttp_settings_s { /* Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_message_begin; /* Possible return values 0, -1, HPE_USER */ llhttp_data_cb on_url; llhttp_data_cb on_status; llhttp_data_cb on_header_field; llhttp_data_cb on_header_value; /* Possible return values: * 0 - Proceed normally * 1 - Assume that request/response has no body, and proceed to parsing the * next message * 2 - Assume absence of body (as above) and make `llhttp_execute()` return * `HPE_PAUSED_UPGRADE` * -1 - Error * `HPE_PAUSED` */ llhttp_cb on_headers_complete; /* Possible return values 0, -1, HPE_USER */ llhttp_data_cb on_body; /* Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_message_complete; /* When on_chunk_header is called, the current chunk length is stored * in parser->content_length. * Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_chunk_header; llhttp_cb on_chunk_complete; /* Information-only callbacks, return value is ignored */ llhttp_cb on_url_complete; llhttp_cb on_status_complete; llhttp_cb on_header_field_complete; llhttp_cb on_header_value_complete; }; /* Initialize the parser with specific type and user settings. * * NOTE: lifetime of `settings` has to be at least the same as the lifetime of * the `parser` here. In practice, `settings` has to be either a static * variable or be allocated with `malloc`, `new`, etc. */ LLHTTP_EXPORT void llhttp_init(llhttp_t* parser, llhttp_type_t type, const llhttp_settings_t* settings); #if defined(__wasm__) LLHTTP_EXPORT llhttp_t* llhttp_alloc(llhttp_type_t type); LLHTTP_EXPORT void llhttp_free(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_type(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_http_major(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_http_minor(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_method(llhttp_t* parser); LLHTTP_EXPORT int llhttp_get_status_code(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_upgrade(llhttp_t* parser); #endif // defined(__wasm__) /* Reset an already initialized parser back to the start state, preserving the * existing parser type, callback settings, user data, and lenient flags. */ LLHTTP_EXPORT void llhttp_reset(llhttp_t* parser); /* Initialize the settings object */ LLHTTP_EXPORT void llhttp_settings_init(llhttp_settings_t* settings); /* Parse full or partial request/response, invoking user callbacks along the * way. * * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing * interrupts, and such errno is returned from `llhttp_execute()`. If * `HPE_PAUSED` was used as a errno, the execution can be resumed with * `llhttp_resume()` call. * * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE` * is returned after fully parsing the request/response. If the user wishes to * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`. * * NOTE: if this function ever returns a non-pause type error, it will continue * to return the same error upon each successive call up until `llhttp_init()` * is called. */ LLHTTP_EXPORT llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len); /* This method should be called when the other side has no further bytes to * send (e.g. shutdown of readable side of the TCP connection.) * * Requests without `Content-Length` and other messages might require treating * all incoming bytes as the part of the body, up to the last byte of the * connection. This method will invoke `on_message_complete()` callback if the * request was terminated safely. Otherwise a error code would be returned. */ LLHTTP_EXPORT llhttp_errno_t llhttp_finish(llhttp_t* parser); /* Returns `1` if the incoming message is parsed until the last byte, and has * to be completed by calling `llhttp_finish()` on EOF */ LLHTTP_EXPORT int llhttp_message_needs_eof(const llhttp_t* parser); /* Returns `1` if there might be any other messages following the last that was * successfully parsed. */ LLHTTP_EXPORT int llhttp_should_keep_alive(const llhttp_t* parser); /* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set * appropriate error reason. * * Important: do not call this from user callbacks! User callbacks must return * `HPE_PAUSED` if pausing is required. */ LLHTTP_EXPORT void llhttp_pause(llhttp_t* parser); /* Might be called to resume the execution after the pause in user's callback. * See `llhttp_execute()` above for details. * * Call this only if `llhttp_execute()` returns `HPE_PAUSED`. */ LLHTTP_EXPORT void llhttp_resume(llhttp_t* parser); /* Might be called to resume the execution after the pause in user's callback. * See `llhttp_execute()` above for details. * * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE` */ LLHTTP_EXPORT void llhttp_resume_after_upgrade(llhttp_t* parser); /* Returns the latest return error */ LLHTTP_EXPORT llhttp_errno_t llhttp_get_errno(const llhttp_t* parser); /* Returns the verbal explanation of the latest returned error. * * Note: User callback should set error reason when returning the error. See * `llhttp_set_error_reason()` for details. */ LLHTTP_EXPORT const char* llhttp_get_error_reason(const llhttp_t* parser); /* Assign verbal description to the returned error. Must be called in user * callbacks right before returning the errno. * * Note: `HPE_USER` error code might be useful in user callbacks. */ LLHTTP_EXPORT void llhttp_set_error_reason(llhttp_t* parser, const char* reason); /* Returns the pointer to the last parsed byte before the returned error. The * pointer is relative to the `data` argument of `llhttp_execute()`. * * Note: this method might be useful for counting the number of parsed bytes. */ LLHTTP_EXPORT const char* llhttp_get_error_pos(const llhttp_t* parser); /* Returns textual name of error code */ LLHTTP_EXPORT const char* llhttp_errno_name(llhttp_errno_t err); /* Returns textual name of HTTP method */ LLHTTP_EXPORT const char* llhttp_method_name(llhttp_method_t method); /* Enables/disables lenient header value parsing (disabled by default). * * Lenient parsing disables header value token checks, extending llhttp's * protocol support to highly non-compliant clients/server. No * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when * lenient parsing is "on". * * **(USE AT YOUR OWN RISK)** */ LLHTTP_EXPORT void llhttp_set_lenient_headers(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of conflicting `Transfer-Encoding` and * `Content-Length` headers (disabled by default). * * Normally `llhttp` would error when `Transfer-Encoding` is present in * conjunction with `Content-Length`. This error is important to prevent HTTP * request smuggling, but may be less desirable for small number of cases * involving legacy servers. * * **(USE AT YOUR OWN RISK)** */ LLHTTP_EXPORT void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of `Connection: close` and HTTP/1.0 * requests responses. * * Normally `llhttp` would error on (in strict mode) or discard (in loose mode) * the HTTP request/response after the request/response with `Connection: close` * and `Content-Length`. This is important to prevent cache poisoning attacks, * but might interact badly with outdated and insecure clients. With this flag * the extra request/response will be parsed normally. * * **(USE AT YOUR OWN RISK)** */ void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* INCLUDE_LLHTTP_API_H_ */ ================================================ FILE: d2mapapi/simphttp/llhttp/http.c ================================================ #include #ifndef LLHTTP__TEST # include "llhttp.h" #else # define llhttp_t llparse_t #endif /* */ int llhttp_message_needs_eof(const llhttp_t* parser); int llhttp_should_keep_alive(const llhttp_t* parser); int llhttp__before_headers_complete(llhttp_t* parser, const char* p, const char* endp) { /* Set this here so that on_headers_complete() callbacks can see it */ if ((parser->flags & F_UPGRADE) && (parser->flags & F_CONNECTION_UPGRADE)) { /* For responses, "Upgrade: foo" and "Connection: upgrade" are * mandatory only when it is a 101 Switching Protocols response, * otherwise it is purely informational, to announce support. */ parser->upgrade = (parser->type == HTTP_REQUEST || parser->status_code == 101); } else { parser->upgrade = (parser->method == HTTP_CONNECT); } return 0; } /* Return values: * 0 - No body, `restart`, message_complete * 1 - CONNECT request, `restart`, message_complete, and pause * 2 - chunk_size_start * 3 - body_identity * 4 - body_identity_eof * 5 - invalid transfer-encoding for request */ int llhttp__after_headers_complete(llhttp_t* parser, const char* p, const char* endp) { int hasBody; hasBody = parser->flags & F_CHUNKED || parser->content_length > 0; if (parser->upgrade && (parser->method == HTTP_CONNECT || (parser->flags & F_SKIPBODY) || !hasBody)) { /* Exit, the rest of the message is in a different protocol. */ return 1; } if (parser->flags & F_SKIPBODY) { return 0; } else if (parser->flags & F_CHUNKED) { /* chunked encoding - ignore Content-Length header, prepare for a chunk */ return 2; } else if (parser->flags & F_TRANSFER_ENCODING) { if (parser->type == HTTP_REQUEST && (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0) { /* RFC 7230 3.3.3 */ /* If a Transfer-Encoding header field * is present in a request and the chunked transfer coding is not * the final encoding, the message body length cannot be determined * reliably; the server MUST respond with the 400 (Bad Request) * status code and then close the connection. */ return 5; } else { /* RFC 7230 3.3.3 */ /* If a Transfer-Encoding header field is present in a response and * the chunked transfer coding is not the final encoding, the * message body length is determined by reading the connection until * it is closed by the server. */ return 4; } } else { if (!(parser->flags & F_CONTENT_LENGTH)) { if (!llhttp_message_needs_eof(parser)) { /* Assume content-length 0 - read the next */ return 0; } else { /* Read body until EOF */ return 4; } } else if (parser->content_length == 0) { /* Content-Length header given but zero: Content-Length: 0\r\n */ return 0; } else { /* Content-Length header given and non-zero */ return 3; } } } int llhttp__after_message_complete(llhttp_t* parser, const char* p, const char* endp) { int should_keep_alive; should_keep_alive = llhttp_should_keep_alive(parser); parser->finish = HTTP_FINISH_SAFE; parser->flags = 0; /* NOTE: this is ignored in loose parsing mode */ return should_keep_alive; } int llhttp_message_needs_eof(const llhttp_t* parser) { if (parser->type == HTTP_REQUEST) { return 0; } /* See RFC 2616 section 4.4 */ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ parser->status_code == 204 || /* No Content */ parser->status_code == 304 || /* Not Modified */ (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */ return 0; } /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */ if ((parser->flags & F_TRANSFER_ENCODING) && (parser->flags & F_CHUNKED) == 0) { return 1; } if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { return 0; } return 1; } int llhttp_should_keep_alive(const llhttp_t* parser) { if (parser->http_major > 0 && parser->http_minor > 0) { /* HTTP/1.1 */ if (parser->flags & F_CONNECTION_CLOSE) { return 0; } } else { /* HTTP/1.0 or earlier */ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { return 0; } } return !llhttp_message_needs_eof(parser); } ================================================ FILE: d2mapapi/simphttp/llhttp/llhttp.c ================================================ #if LLHTTP_STRICT_MODE #include #include #include #ifdef __SSE4_2__ #ifdef _MSC_VER #include #else /* !_MSC_VER */ #include #endif /* _MSC_VER */ #endif /* __SSE4_2__ */ #ifdef _MSC_VER #define ALIGN(n) _declspec(align(n)) #else /* !_MSC_VER */ #define ALIGN(n) __attribute__((aligned(n))) #endif /* _MSC_VER */ #include "llhttp.h" typedef int (*llhttp__internal__span_cb)( llhttp__internal_t*, const char*, const char*); static const unsigned char llparse_blob0[] = { 0xd, 0xa }; static const unsigned char llparse_blob1[] = { 'o', 'n' }; static const unsigned char llparse_blob2[] = { 'e', 'c', 't', 'i', 'o', 'n' }; static const unsigned char llparse_blob3[] = { 'l', 'o', 's', 'e' }; static const unsigned char llparse_blob4[] = { 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' }; static const unsigned char llparse_blob5[] = { 'p', 'g', 'r', 'a', 'd', 'e' }; static const unsigned char llparse_blob6[] = { 'c', 'h', 'u', 'n', 'k', 'e', 'd' }; #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob7[] = { 0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; #endif /* __SSE4_2__ */ #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob8[] = { '!', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', 'Z', '^', 'z', '|', '|' }; #endif /* __SSE4_2__ */ #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob9[] = { '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; #endif /* __SSE4_2__ */ static const unsigned char llparse_blob10[] = { 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' }; static const unsigned char llparse_blob11[] = { 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n' }; static const unsigned char llparse_blob12[] = { 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g' }; static const unsigned char llparse_blob13[] = { 'p', 'g', 'r', 'a', 'd', 'e' }; static const unsigned char llparse_blob14[] = { 'T', 'T', 'P', '/' }; static const unsigned char llparse_blob15[] = { 0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa }; static const unsigned char llparse_blob16[] = { 'C', 'E', '/' }; static const unsigned char llparse_blob17[] = { 'T', 'S', 'P', '/' }; static const unsigned char llparse_blob18[] = { 'N', 'O', 'U', 'N', 'C', 'E' }; static const unsigned char llparse_blob19[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob20[] = { 'E', 'C', 'K', 'O', 'U', 'T' }; static const unsigned char llparse_blob21[] = { 'N', 'E', 'C', 'T' }; static const unsigned char llparse_blob22[] = { 'E', 'T', 'E' }; static const unsigned char llparse_blob23[] = { 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob24[] = { 'L', 'U', 'S', 'H' }; static const unsigned char llparse_blob25[] = { 'E', 'T' }; static const unsigned char llparse_blob26[] = { 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' }; static const unsigned char llparse_blob27[] = { 'E', 'A', 'D' }; static const unsigned char llparse_blob28[] = { 'N', 'K' }; static const unsigned char llparse_blob29[] = { 'C', 'K' }; static const unsigned char llparse_blob30[] = { 'S', 'E', 'A', 'R', 'C', 'H' }; static const unsigned char llparse_blob31[] = { 'R', 'G', 'E' }; static const unsigned char llparse_blob32[] = { 'C', 'T', 'I', 'V', 'I', 'T', 'Y' }; static const unsigned char llparse_blob33[] = { 'L', 'E', 'N', 'D', 'A', 'R' }; static const unsigned char llparse_blob34[] = { 'V', 'E' }; static const unsigned char llparse_blob35[] = { 'O', 'T', 'I', 'F', 'Y' }; static const unsigned char llparse_blob36[] = { 'P', 'T', 'I', 'O', 'N', 'S' }; static const unsigned char llparse_blob37[] = { 'C', 'H' }; static const unsigned char llparse_blob38[] = { 'S', 'E' }; static const unsigned char llparse_blob39[] = { 'A', 'Y' }; static const unsigned char llparse_blob40[] = { 'S', 'T' }; static const unsigned char llparse_blob41[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob42[] = { 'A', 'T', 'C', 'H' }; static const unsigned char llparse_blob43[] = { 'G', 'E' }; static const unsigned char llparse_blob44[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob45[] = { 'O', 'R', 'D' }; static const unsigned char llparse_blob46[] = { 'I', 'R', 'E', 'C', 'T' }; static const unsigned char llparse_blob47[] = { 'O', 'R', 'T' }; static const unsigned char llparse_blob48[] = { 'R', 'C', 'H' }; static const unsigned char llparse_blob49[] = { 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' }; static const unsigned char llparse_blob50[] = { 'U', 'R', 'C', 'E' }; static const unsigned char llparse_blob51[] = { 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob52[] = { 'A', 'R', 'D', 'O', 'W', 'N' }; static const unsigned char llparse_blob53[] = { 'A', 'C', 'E' }; static const unsigned char llparse_blob54[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob55[] = { 'N', 'K' }; static const unsigned char llparse_blob56[] = { 'C', 'K' }; static const unsigned char llparse_blob57[] = { 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob58[] = { 'H', 'T', 'T', 'P', '/' }; static const unsigned char llparse_blob59[] = { 'A', 'D' }; static const unsigned char llparse_blob60[] = { 'T', 'P', '/' }; enum llparse_match_status_e { kMatchComplete, kMatchPause, kMatchMismatch }; typedef enum llparse_match_status_e llparse_match_status_t; struct llparse_match_s { llparse_match_status_t status; const unsigned char* current; }; typedef struct llparse_match_s llparse_match_t; static llparse_match_t llparse__match_sequence_id( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = *p; if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } static llparse_match_t llparse__match_sequence_to_lower( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p)); if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } static llparse_match_t llparse__match_sequence_to_lower_unsafe( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = ((*p) | 0x20); if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } enum llparse_state_e { s_error, s_n_llhttp__internal__n_closed, s_n_llhttp__internal__n_invoke_llhttp__after_message_complete, s_n_llhttp__internal__n_pause_1, s_n_llhttp__internal__n_invoke_is_equal_upgrade, s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2, s_n_llhttp__internal__n_chunk_data_almost_done, s_n_llhttp__internal__n_consume_content_length, s_n_llhttp__internal__n_span_start_llhttp__on_body, s_n_llhttp__internal__n_invoke_is_equal_content_length, s_n_llhttp__internal__n_chunk_size_almost_done, s_n_llhttp__internal__n_chunk_parameters, s_n_llhttp__internal__n_chunk_size_otherwise, s_n_llhttp__internal__n_chunk_size, s_n_llhttp__internal__n_chunk_size_digit, s_n_llhttp__internal__n_invoke_update_content_length, s_n_llhttp__internal__n_consume_content_length_1, s_n_llhttp__internal__n_span_start_llhttp__on_body_1, s_n_llhttp__internal__n_eof, s_n_llhttp__internal__n_span_start_llhttp__on_body_2, s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete, s_n_llhttp__internal__n_headers_almost_done, s_n_llhttp__internal__n_header_field_colon_discard_ws, s_n_llhttp__internal__n_error_19, s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete, s_n_llhttp__internal__n_span_start_llhttp__on_header_value, s_n_llhttp__internal__n_header_value_discard_lws, s_n_llhttp__internal__n_header_value_discard_ws_almost_done, s_n_llhttp__internal__n_header_value_lws, s_n_llhttp__internal__n_header_value_almost_done, s_n_llhttp__internal__n_header_value_lenient, s_n_llhttp__internal__n_header_value_otherwise, s_n_llhttp__internal__n_header_value_connection_token, s_n_llhttp__internal__n_header_value_connection_ws, s_n_llhttp__internal__n_header_value_connection_1, s_n_llhttp__internal__n_header_value_connection_2, s_n_llhttp__internal__n_header_value_connection_3, s_n_llhttp__internal__n_header_value_connection, s_n_llhttp__internal__n_error_25, s_n_llhttp__internal__n_error_26, s_n_llhttp__internal__n_header_value_content_length_ws, s_n_llhttp__internal__n_header_value_content_length, s_n_llhttp__internal__n_header_value_te_chunked_last, s_n_llhttp__internal__n_header_value_te_token_ows, s_n_llhttp__internal__n_header_value, s_n_llhttp__internal__n_header_value_te_token, s_n_llhttp__internal__n_header_value_te_chunked, s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1, s_n_llhttp__internal__n_header_value_discard_ws, s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete, s_n_llhttp__internal__n_header_field_general_otherwise, s_n_llhttp__internal__n_header_field_general, s_n_llhttp__internal__n_header_field_colon, s_n_llhttp__internal__n_header_field_3, s_n_llhttp__internal__n_header_field_4, s_n_llhttp__internal__n_header_field_2, s_n_llhttp__internal__n_header_field_1, s_n_llhttp__internal__n_header_field_5, s_n_llhttp__internal__n_header_field_6, s_n_llhttp__internal__n_header_field_7, s_n_llhttp__internal__n_header_field, s_n_llhttp__internal__n_span_start_llhttp__on_header_field, s_n_llhttp__internal__n_header_field_start, s_n_llhttp__internal__n_url_to_http_09, s_n_llhttp__internal__n_url_skip_to_http09, s_n_llhttp__internal__n_url_skip_lf_to_http09_1, s_n_llhttp__internal__n_url_skip_lf_to_http09, s_n_llhttp__internal__n_req_pri_upgrade, s_n_llhttp__internal__n_req_http_complete_1, s_n_llhttp__internal__n_req_http_complete, s_n_llhttp__internal__n_req_http_minor, s_n_llhttp__internal__n_req_http_dot, s_n_llhttp__internal__n_req_http_major, s_n_llhttp__internal__n_req_http_start_1, s_n_llhttp__internal__n_req_http_start_2, s_n_llhttp__internal__n_req_http_start_3, s_n_llhttp__internal__n_req_http_start, s_n_llhttp__internal__n_url_to_http, s_n_llhttp__internal__n_url_skip_to_http, s_n_llhttp__internal__n_url_fragment, s_n_llhttp__internal__n_span_end_stub_query_3, s_n_llhttp__internal__n_url_query, s_n_llhttp__internal__n_url_query_or_fragment, s_n_llhttp__internal__n_url_path, s_n_llhttp__internal__n_span_start_stub_path_2, s_n_llhttp__internal__n_span_start_stub_path, s_n_llhttp__internal__n_span_start_stub_path_1, s_n_llhttp__internal__n_url_server_with_at, s_n_llhttp__internal__n_url_server, s_n_llhttp__internal__n_url_schema_delim_1, s_n_llhttp__internal__n_url_schema_delim, s_n_llhttp__internal__n_span_end_stub_schema, s_n_llhttp__internal__n_url_schema, s_n_llhttp__internal__n_url_start, s_n_llhttp__internal__n_span_start_llhttp__on_url_1, s_n_llhttp__internal__n_url_entry_normal, s_n_llhttp__internal__n_span_start_llhttp__on_url, s_n_llhttp__internal__n_url_entry_connect, s_n_llhttp__internal__n_req_spaces_before_url, s_n_llhttp__internal__n_req_first_space_before_url, s_n_llhttp__internal__n_start_req_2, s_n_llhttp__internal__n_start_req_3, s_n_llhttp__internal__n_start_req_1, s_n_llhttp__internal__n_start_req_4, s_n_llhttp__internal__n_start_req_6, s_n_llhttp__internal__n_start_req_8, s_n_llhttp__internal__n_start_req_9, s_n_llhttp__internal__n_start_req_7, s_n_llhttp__internal__n_start_req_5, s_n_llhttp__internal__n_start_req_12, s_n_llhttp__internal__n_start_req_13, s_n_llhttp__internal__n_start_req_11, s_n_llhttp__internal__n_start_req_10, s_n_llhttp__internal__n_start_req_14, s_n_llhttp__internal__n_start_req_17, s_n_llhttp__internal__n_start_req_16, s_n_llhttp__internal__n_start_req_15, s_n_llhttp__internal__n_start_req_18, s_n_llhttp__internal__n_start_req_20, s_n_llhttp__internal__n_start_req_21, s_n_llhttp__internal__n_start_req_19, s_n_llhttp__internal__n_start_req_23, s_n_llhttp__internal__n_start_req_24, s_n_llhttp__internal__n_start_req_26, s_n_llhttp__internal__n_start_req_28, s_n_llhttp__internal__n_start_req_29, s_n_llhttp__internal__n_start_req_27, s_n_llhttp__internal__n_start_req_25, s_n_llhttp__internal__n_start_req_30, s_n_llhttp__internal__n_start_req_22, s_n_llhttp__internal__n_start_req_31, s_n_llhttp__internal__n_start_req_32, s_n_llhttp__internal__n_start_req_35, s_n_llhttp__internal__n_start_req_36, s_n_llhttp__internal__n_start_req_34, s_n_llhttp__internal__n_start_req_37, s_n_llhttp__internal__n_start_req_38, s_n_llhttp__internal__n_start_req_42, s_n_llhttp__internal__n_start_req_43, s_n_llhttp__internal__n_start_req_41, s_n_llhttp__internal__n_start_req_40, s_n_llhttp__internal__n_start_req_39, s_n_llhttp__internal__n_start_req_45, s_n_llhttp__internal__n_start_req_44, s_n_llhttp__internal__n_start_req_33, s_n_llhttp__internal__n_start_req_48, s_n_llhttp__internal__n_start_req_49, s_n_llhttp__internal__n_start_req_50, s_n_llhttp__internal__n_start_req_51, s_n_llhttp__internal__n_start_req_47, s_n_llhttp__internal__n_start_req_46, s_n_llhttp__internal__n_start_req_54, s_n_llhttp__internal__n_start_req_56, s_n_llhttp__internal__n_start_req_57, s_n_llhttp__internal__n_start_req_55, s_n_llhttp__internal__n_start_req_53, s_n_llhttp__internal__n_start_req_58, s_n_llhttp__internal__n_start_req_59, s_n_llhttp__internal__n_start_req_52, s_n_llhttp__internal__n_start_req_61, s_n_llhttp__internal__n_start_req_62, s_n_llhttp__internal__n_start_req_60, s_n_llhttp__internal__n_start_req_65, s_n_llhttp__internal__n_start_req_67, s_n_llhttp__internal__n_start_req_68, s_n_llhttp__internal__n_start_req_66, s_n_llhttp__internal__n_start_req_69, s_n_llhttp__internal__n_start_req_64, s_n_llhttp__internal__n_start_req_63, s_n_llhttp__internal__n_start_req, s_n_llhttp__internal__n_invoke_llhttp__on_status_complete, s_n_llhttp__internal__n_res_line_almost_done, s_n_llhttp__internal__n_res_status, s_n_llhttp__internal__n_span_start_llhttp__on_status, s_n_llhttp__internal__n_res_status_start, s_n_llhttp__internal__n_res_status_code_otherwise, s_n_llhttp__internal__n_res_status_code, s_n_llhttp__internal__n_res_http_end, s_n_llhttp__internal__n_res_http_minor, s_n_llhttp__internal__n_res_http_dot, s_n_llhttp__internal__n_res_http_major, s_n_llhttp__internal__n_start_res, s_n_llhttp__internal__n_req_or_res_method_2, s_n_llhttp__internal__n_req_or_res_method_3, s_n_llhttp__internal__n_req_or_res_method_1, s_n_llhttp__internal__n_req_or_res_method, s_n_llhttp__internal__n_start_req_or_res, s_n_llhttp__internal__n_invoke_load_type, s_n_llhttp__internal__n_start, }; typedef enum llparse_state_e llparse_state_t; int llhttp__on_url( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_header_field( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_header_value( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_body( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_status( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_finish( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 2; return 0; } int llhttp__on_message_begin( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_type( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->type; } int llhttp__internal__c_store_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->method = match; return 0; } int llhttp__internal__c_is_equal_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->method == 5; } int llhttp__internal__c_update_http_major( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->http_major = 0; return 0; } int llhttp__internal__c_update_http_minor( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->http_minor = 9; return 0; } int llhttp__on_url_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_test_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 128) == 128; } int llhttp__on_chunk_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_message_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_is_equal_upgrade( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->upgrade == 1; } int llhttp__after_message_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_finish_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 0; return 0; } int llhttp__internal__c_test_lenient_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 4) == 4; } int llhttp__internal__c_test_flags_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 544) == 544; } int llhttp__internal__c_test_lenient_flags_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 2) == 2; } int llhttp__before_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__after_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->content_length = 0; return 0; } int llhttp__internal__c_mul_add_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->content_length > 0xffffffffffffffffULL / 16) { return 1; } state->content_length *= 16; /* Addition overflow */ if (match >= 0) { if (state->content_length > 0xffffffffffffffffULL - match) { return 1; } } else { if (state->content_length < 0ULL - match) { return 1; } } state->content_length += match; return 0; } int llhttp__on_chunk_header( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_is_equal_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->content_length == 0; } int llhttp__internal__c_or_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 128; return 0; } int llhttp__internal__c_update_finish_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 1; return 0; } int llhttp__internal__c_or_flags_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 64; return 0; } int llhttp__internal__c_update_upgrade( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->upgrade = 1; return 0; } int llhttp__internal__c_store_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->header_state = match; return 0; } int llhttp__internal__c_test_lenient_flags_2( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 1) == 1; } int llhttp__on_header_field_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->header_state; } int llhttp__internal__c_or_flags_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 1; return 0; } int llhttp__internal__c_update_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 1; return 0; } int llhttp__on_header_value_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_or_flags_4( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 2; return 0; } int llhttp__internal__c_or_flags_5( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 4; return 0; } int llhttp__internal__c_or_flags_6( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 8; return 0; } int llhttp__internal__c_update_header_state_2( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 6; return 0; } int llhttp__internal__c_update_header_state_4( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 0; return 0; } int llhttp__internal__c_update_header_state_5( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 5; return 0; } int llhttp__internal__c_update_header_state_6( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 7; return 0; } int llhttp__internal__c_test_flags_2( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 32) == 32; } int llhttp__internal__c_mul_add_content_length_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->content_length > 0xffffffffffffffffULL / 10) { return 1; } state->content_length *= 10; /* Addition overflow */ if (match >= 0) { if (state->content_length > 0xffffffffffffffffULL - match) { return 1; } } else { if (state->content_length < 0ULL - match) { return 1; } } state->content_length += match; return 0; } int llhttp__internal__c_or_flags_15( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 32; return 0; } int llhttp__internal__c_or_flags_16( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 512; return 0; } int llhttp__internal__c_and_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags &= -9; return 0; } int llhttp__internal__c_update_header_state_7( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 8; return 0; } int llhttp__internal__c_or_flags_17( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 16; return 0; } int llhttp__internal__c_load_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->method; } int llhttp__internal__c_store_http_major( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->http_major = match; return 0; } int llhttp__internal__c_store_http_minor( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->http_minor = match; return 0; } int llhttp__internal__c_update_status_code( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->status_code = 0; return 0; } int llhttp__internal__c_mul_add_status_code( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->status_code > 0xffff / 10) { return 1; } state->status_code *= 10; /* Addition overflow */ if (match >= 0) { if (state->status_code > 0xffff - match) { return 1; } } else { if (state->status_code < 0 - match) { return 1; } } state->status_code += match; /* Enforce maximum */ if (state->status_code > 999) { return 1; } return 0; } int llhttp__on_status_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_type( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->type = 1; return 0; } int llhttp__internal__c_update_type_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->type = 2; return 0; } int llhttp__internal_init(llhttp__internal_t* state) { memset(state, 0, sizeof(*state)); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start; return 0; } static llparse_state_t llhttp__internal__run( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { int match; switch ((llparse_state_t) (intptr_t) state->_current) { case s_n_llhttp__internal__n_closed: s_n_llhttp__internal__n_closed: { if (p == endp) { return s_n_llhttp__internal__n_closed; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_closed; } case 13: { p++; goto s_n_llhttp__internal__n_closed; } default: { p++; goto s_n_llhttp__internal__n_error_4; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: { switch (llhttp__after_message_complete(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_update_finish_2; default: goto s_n_llhttp__internal__n_invoke_update_finish_1; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_pause_1: s_n_llhttp__internal__n_pause_1: { state->error = 0x16; state->reason = "Pause on CONNECT/Upgrade"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; return s_error; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_is_equal_upgrade: s_n_llhttp__internal__n_invoke_is_equal_upgrade: { switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; default: goto s_n_llhttp__internal__n_pause_1; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_is_equal_upgrade; case 21: goto s_n_llhttp__internal__n_pause_5; default: goto s_n_llhttp__internal__n_error_14; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_data_almost_done: s_n_llhttp__internal__n_chunk_data_almost_done: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_chunk_data_almost_done; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob0, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; } case kMatchPause: { return s_n_llhttp__internal__n_chunk_data_almost_done; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_8; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_consume_content_length: s_n_llhttp__internal__n_consume_content_length: { size_t avail; uint64_t need; avail = endp - p; need = state->content_length; if (avail >= need) { p += need; state->content_length = 0; goto s_n_llhttp__internal__n_span_end_llhttp__on_body; } state->content_length -= avail; return s_n_llhttp__internal__n_consume_content_length; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_body: s_n_llhttp__internal__n_span_start_llhttp__on_body: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_consume_content_length; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_is_equal_content_length: s_n_llhttp__internal__n_invoke_is_equal_content_length: { switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_start_llhttp__on_body; default: goto s_n_llhttp__internal__n_invoke_or_flags; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_size_almost_done: s_n_llhttp__internal__n_chunk_size_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; } default: { goto s_n_llhttp__internal__n_error_9; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_parameters: s_n_llhttp__internal__n_chunk_parameters: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_chunk_parameters; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_chunk_parameters; } case 2: { p++; goto s_n_llhttp__internal__n_chunk_size_almost_done; } default: { goto s_n_llhttp__internal__n_error_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_size_otherwise: s_n_llhttp__internal__n_chunk_size_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_otherwise; } switch (*p) { case 13: { p++; goto s_n_llhttp__internal__n_chunk_size_almost_done; } case ' ': { p++; goto s_n_llhttp__internal__n_chunk_parameters; } case ';': { p++; goto s_n_llhttp__internal__n_chunk_parameters; } default: { goto s_n_llhttp__internal__n_error_11; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_size: s_n_llhttp__internal__n_chunk_size: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'A': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'B': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'C': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'D': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'E': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'F': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'a': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'b': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'c': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'd': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'e': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'f': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } default: { goto s_n_llhttp__internal__n_chunk_size_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_size_digit: s_n_llhttp__internal__n_chunk_size_digit: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_digit; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'A': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'B': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'C': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'D': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'E': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'F': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'a': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'b': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'c': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'd': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'e': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'f': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } default: { goto s_n_llhttp__internal__n_error_13; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_update_content_length: s_n_llhttp__internal__n_invoke_update_content_length: { switch (llhttp__internal__c_update_content_length(state, p, endp)) { default: goto s_n_llhttp__internal__n_chunk_size_digit; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_consume_content_length_1: s_n_llhttp__internal__n_consume_content_length_1: { size_t avail; uint64_t need; avail = endp - p; need = state->content_length; if (avail >= need) { p += need; state->content_length = 0; goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1; } state->content_length -= avail; return s_n_llhttp__internal__n_consume_content_length_1; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_body_1: s_n_llhttp__internal__n_span_start_llhttp__on_body_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_consume_content_length_1; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_eof: s_n_llhttp__internal__n_eof: { if (p == endp) { return s_n_llhttp__internal__n_eof; } p++; goto s_n_llhttp__internal__n_eof; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_body_2: s_n_llhttp__internal__n_span_start_llhttp__on_body_2: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body_2; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_eof; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: { switch (llhttp__after_headers_complete(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1; case 2: goto s_n_llhttp__internal__n_invoke_update_content_length; case 3: goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1; case 4: goto s_n_llhttp__internal__n_invoke_update_finish_3; case 5: goto s_n_llhttp__internal__n_error_15; default: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_headers_almost_done: s_n_llhttp__internal__n_headers_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_headers_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_test_flags; } default: { goto s_n_llhttp__internal__n_error_18; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_colon_discard_ws: s_n_llhttp__internal__n_header_field_colon_discard_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_field_colon_discard_ws; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_header_field_colon_discard_ws; } default: { goto s_n_llhttp__internal__n_header_field_colon; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_error_19: s_n_llhttp__internal__n_error_19: { state->error = 0xa; state->reason = "Invalid header field char"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: { switch (llhttp__on_header_value_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_header_value: s_n_llhttp__internal__n_span_start_llhttp__on_header_value: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_value; goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_discard_lws: s_n_llhttp__internal__n_header_value_discard_lws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_lws; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } default: { goto s_n_llhttp__internal__n_invoke_load_header_state; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_discard_ws_almost_done: s_n_llhttp__internal__n_header_value_discard_ws_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_ws_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_value_discard_lws; } default: { goto s_n_llhttp__internal__n_error_21; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_lws: s_n_llhttp__internal__n_header_value_lws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_lws; } switch (*p) { case 9: { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } case ' ': { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } default: { goto s_n_llhttp__internal__n_invoke_load_header_state_3; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_almost_done: s_n_llhttp__internal__n_header_value_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_header_value_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_value_lws; } default: { goto s_n_llhttp__internal__n_error_22; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_lenient: s_n_llhttp__internal__n_header_value_lenient: { if (p == endp) { return s_n_llhttp__internal__n_header_value_lenient; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; } default: { p++; goto s_n_llhttp__internal__n_header_value_lenient; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_otherwise: s_n_llhttp__internal__n_header_value_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_header_value_otherwise; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_token: s_n_llhttp__internal__n_header_value_connection_token: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_token; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value_connection_token; } case 2: { p++; goto s_n_llhttp__internal__n_header_value_connection; } default: { goto s_n_llhttp__internal__n_header_value_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_ws: s_n_llhttp__internal__n_header_value_connection_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_ws; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_header_value_otherwise; } case 13: { goto s_n_llhttp__internal__n_header_value_otherwise; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_connection_ws; } case ',': { p++; goto s_n_llhttp__internal__n_invoke_load_header_state_4; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_4; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_1: s_n_llhttp__internal__n_header_value_connection_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_1; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_2; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_2: s_n_llhttp__internal__n_header_value_connection_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_2; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_5; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_3: s_n_llhttp__internal__n_header_value_connection_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_3; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob5, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_6; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection: s_n_llhttp__internal__n_header_value_connection: { if (p == endp) { return s_n_llhttp__internal__n_header_value_connection; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_connection; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_connection; } case 'c': { p++; goto s_n_llhttp__internal__n_header_value_connection_1; } case 'k': { p++; goto s_n_llhttp__internal__n_header_value_connection_2; } case 'u': { p++; goto s_n_llhttp__internal__n_header_value_connection_3; } default: { goto s_n_llhttp__internal__n_header_value_connection_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_error_25: s_n_llhttp__internal__n_error_25: { state->error = 0xb; state->reason = "Content-Length overflow"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_error_26: s_n_llhttp__internal__n_error_26: { state->error = 0xb; state->reason = "Invalid character in Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_content_length_ws: s_n_llhttp__internal__n_header_value_content_length_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_content_length_ws; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_invoke_or_flags_15; } case 13: { goto s_n_llhttp__internal__n_invoke_or_flags_15; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_content_length_ws; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_content_length: s_n_llhttp__internal__n_header_value_content_length: { if (p == endp) { return s_n_llhttp__internal__n_header_value_content_length; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } default: { goto s_n_llhttp__internal__n_header_value_content_length_ws; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_te_chunked_last: s_n_llhttp__internal__n_header_value_te_chunked_last: { if (p == endp) { return s_n_llhttp__internal__n_header_value_te_chunked_last; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_invoke_update_header_state_7; } case 13: { goto s_n_llhttp__internal__n_invoke_update_header_state_7; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_te_chunked_last; } default: { goto s_n_llhttp__internal__n_header_value_te_chunked; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_te_token_ows: s_n_llhttp__internal__n_header_value_te_token_ows: { if (p == endp) { return s_n_llhttp__internal__n_header_value_te_token_ows; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } default: { goto s_n_llhttp__internal__n_header_value_te_chunked; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value: s_n_llhttp__internal__n_header_value: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value; } #ifdef __SSE4_2__ if (endp - p >= 16) { __m128i ranges; __m128i input; int avail; int match_len; /* Load input */ input = _mm_loadu_si128((__m128i const*) p); ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 6, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_value; } goto s_n_llhttp__internal__n_header_value_otherwise; } #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value; } default: { goto s_n_llhttp__internal__n_header_value_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_te_token: s_n_llhttp__internal__n_header_value_te_token: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value_te_token; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value_te_token; } case 2: { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_8; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_te_chunked: s_n_llhttp__internal__n_header_value_te_chunked: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_te_chunked; } match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_header_value_te_chunked_last; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_te_chunked; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_te_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_value; goto s_n_llhttp__internal__n_invoke_load_header_state_2; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_discard_ws: s_n_llhttp__internal__n_header_value_discard_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_ws; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } case 10: { p++; goto s_n_llhttp__internal__n_header_value_discard_lws; } case 13: { p++; goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: { switch (llhttp__on_header_field_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_discard_ws; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_general_otherwise: s_n_llhttp__internal__n_header_field_general_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_header_field_general_otherwise; } switch (*p) { case ':': { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2; } default: { goto s_n_llhttp__internal__n_error_27; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_general: s_n_llhttp__internal__n_header_field_general: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_header_field_general; } #ifdef __SSE4_2__ if (endp - p >= 16) { __m128i ranges; __m128i input; int avail; int match_len; /* Load input */ input = _mm_loadu_si128((__m128i const*) p); ranges = _mm_loadu_si128((__m128i const*) llparse_blob8); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 16, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_field_general; } ranges = _mm_loadu_si128((__m128i const*) llparse_blob9); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 2, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_field_general; } goto s_n_llhttp__internal__n_header_field_general_otherwise; } #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_field_general; } default: { goto s_n_llhttp__internal__n_header_field_general_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_colon: s_n_llhttp__internal__n_header_field_colon: { if (p == endp) { return s_n_llhttp__internal__n_header_field_colon; } switch (*p) { case ' ': { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2; } case ':': { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_9; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_3: s_n_llhttp__internal__n_header_field_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_3; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_4: s_n_llhttp__internal__n_header_field_4: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_4; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 10); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_4; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_2: s_n_llhttp__internal__n_header_field_2: { if (p == endp) { return s_n_llhttp__internal__n_header_field_2; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 'n': { p++; goto s_n_llhttp__internal__n_header_field_3; } case 't': { p++; goto s_n_llhttp__internal__n_header_field_4; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_1: s_n_llhttp__internal__n_header_field_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_1; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_header_field_2; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_5: s_n_llhttp__internal__n_header_field_5: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_5; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 15); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_5; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_6: s_n_llhttp__internal__n_header_field_6: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_6; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 16); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_6; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_7: s_n_llhttp__internal__n_header_field_7: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_7; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob13, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_7; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field: s_n_llhttp__internal__n_header_field: { if (p == endp) { return s_n_llhttp__internal__n_header_field; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 'c': { p++; goto s_n_llhttp__internal__n_header_field_1; } case 'p': { p++; goto s_n_llhttp__internal__n_header_field_5; } case 't': { p++; goto s_n_llhttp__internal__n_header_field_6; } case 'u': { p++; goto s_n_llhttp__internal__n_header_field_7; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_header_field: s_n_llhttp__internal__n_span_start_llhttp__on_header_field: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_field; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_field; goto s_n_llhttp__internal__n_header_field; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_start: s_n_llhttp__internal__n_header_field_start: { if (p == endp) { return s_n_llhttp__internal__n_header_field_start; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_headers_almost_done; } case 13: { p++; goto s_n_llhttp__internal__n_headers_almost_done; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_to_http_09: s_n_llhttp__internal__n_url_to_http_09: { if (p == endp) { return s_n_llhttp__internal__n_url_to_http_09; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } default: { goto s_n_llhttp__internal__n_invoke_update_http_major; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_skip_to_http09: s_n_llhttp__internal__n_url_skip_to_http09: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_to_http09; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } default: { p++; goto s_n_llhttp__internal__n_url_to_http_09; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_skip_lf_to_http09_1: s_n_llhttp__internal__n_url_skip_lf_to_http09_1: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_lf_to_http09_1; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_url_to_http_09; } default: { goto s_n_llhttp__internal__n_error_28; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_skip_lf_to_http09: s_n_llhttp__internal__n_url_skip_lf_to_http09: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_lf_to_http09; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } case 13: { p++; goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1; } default: { goto s_n_llhttp__internal__n_error_28; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_pri_upgrade: s_n_llhttp__internal__n_req_pri_upgrade: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_pri_upgrade; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 10); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_error_31; } case kMatchPause: { return s_n_llhttp__internal__n_req_pri_upgrade; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_32; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_complete_1: s_n_llhttp__internal__n_req_http_complete_1: { if (p == endp) { return s_n_llhttp__internal__n_req_http_complete_1; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_field_start; } default: { goto s_n_llhttp__internal__n_error_30; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_complete: s_n_llhttp__internal__n_req_http_complete: { if (p == endp) { return s_n_llhttp__internal__n_req_http_complete; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_field_start; } case 13: { p++; goto s_n_llhttp__internal__n_req_http_complete_1; } default: { goto s_n_llhttp__internal__n_error_30; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_minor: s_n_llhttp__internal__n_req_http_minor: { if (p == endp) { return s_n_llhttp__internal__n_req_http_minor; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_minor; } default: { goto s_n_llhttp__internal__n_error_33; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_dot: s_n_llhttp__internal__n_req_http_dot: { if (p == endp) { return s_n_llhttp__internal__n_req_http_dot; } switch (*p) { case '.': { p++; goto s_n_llhttp__internal__n_req_http_minor; } default: { goto s_n_llhttp__internal__n_error_34; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_major: s_n_llhttp__internal__n_req_http_major: { if (p == endp) { return s_n_llhttp__internal__n_req_http_major; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_major; } default: { goto s_n_llhttp__internal__n_error_35; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_start_1: s_n_llhttp__internal__n_req_http_start_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_http_start_1; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_load_method; } case kMatchPause: { return s_n_llhttp__internal__n_req_http_start_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_38; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_start_2: s_n_llhttp__internal__n_req_http_start_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_http_start_2; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_load_method_2; } case kMatchPause: { return s_n_llhttp__internal__n_req_http_start_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_38; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_start_3: s_n_llhttp__internal__n_req_http_start_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_http_start_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_load_method_3; } case kMatchPause: { return s_n_llhttp__internal__n_req_http_start_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_38; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_start: s_n_llhttp__internal__n_req_http_start: { if (p == endp) { return s_n_llhttp__internal__n_req_http_start; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_http_start; } case 'H': { p++; goto s_n_llhttp__internal__n_req_http_start_1; } case 'I': { p++; goto s_n_llhttp__internal__n_req_http_start_2; } case 'R': { p++; goto s_n_llhttp__internal__n_req_http_start_3; } default: { goto s_n_llhttp__internal__n_error_38; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_to_http: s_n_llhttp__internal__n_url_to_http: { if (p == endp) { return s_n_llhttp__internal__n_url_to_http; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } default: { goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_skip_to_http: s_n_llhttp__internal__n_url_skip_to_http: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_to_http; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } default: { p++; goto s_n_llhttp__internal__n_url_to_http; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_fragment: s_n_llhttp__internal__n_url_fragment: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_fragment; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_1; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8; } case 5: { p++; goto s_n_llhttp__internal__n_url_fragment; } default: { goto s_n_llhttp__internal__n_error_39; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_end_stub_query_3: s_n_llhttp__internal__n_span_end_stub_query_3: { if (p == endp) { return s_n_llhttp__internal__n_span_end_stub_query_3; } p++; goto s_n_llhttp__internal__n_url_fragment; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_query: s_n_llhttp__internal__n_url_query: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_query; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_1; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11; } case 5: { p++; goto s_n_llhttp__internal__n_url_query; } case 6: { goto s_n_llhttp__internal__n_span_end_stub_query_3; } default: { goto s_n_llhttp__internal__n_error_40; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_query_or_fragment: s_n_llhttp__internal__n_url_query_or_fragment: { if (p == endp) { return s_n_llhttp__internal__n_url_query_or_fragment; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4; } case ' ': { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5; } case '#': { p++; goto s_n_llhttp__internal__n_url_fragment; } case '?': { p++; goto s_n_llhttp__internal__n_url_query; } default: { goto s_n_llhttp__internal__n_error_41; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_path: s_n_llhttp__internal__n_url_path: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_path; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_1; } case 2: { p++; goto s_n_llhttp__internal__n_url_path; } default: { goto s_n_llhttp__internal__n_url_query_or_fragment; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_stub_path_2: s_n_llhttp__internal__n_span_start_stub_path_2: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path_2; } p++; goto s_n_llhttp__internal__n_url_path; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_stub_path: s_n_llhttp__internal__n_span_start_stub_path: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path; } p++; goto s_n_llhttp__internal__n_url_path; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_stub_path_1: s_n_llhttp__internal__n_span_start_stub_path_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path_1; } p++; goto s_n_llhttp__internal__n_url_path; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_server_with_at: s_n_llhttp__internal__n_url_server_with_at: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_server_with_at; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_1; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14; } case 5: { p++; goto s_n_llhttp__internal__n_url_server; } case 6: { goto s_n_llhttp__internal__n_span_start_stub_path_1; } case 7: { p++; goto s_n_llhttp__internal__n_url_query; } case 8: { p++; goto s_n_llhttp__internal__n_error_42; } default: { goto s_n_llhttp__internal__n_error_43; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_server: s_n_llhttp__internal__n_url_server: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_server; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_1; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2; } case 5: { p++; goto s_n_llhttp__internal__n_url_server; } case 6: { goto s_n_llhttp__internal__n_span_start_stub_path; } case 7: { p++; goto s_n_llhttp__internal__n_url_query; } case 8: { p++; goto s_n_llhttp__internal__n_url_server_with_at; } default: { goto s_n_llhttp__internal__n_error_44; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_schema_delim_1: s_n_llhttp__internal__n_url_schema_delim_1: { if (p == endp) { return s_n_llhttp__internal__n_url_schema_delim_1; } switch (*p) { case '/': { p++; goto s_n_llhttp__internal__n_url_server; } default: { goto s_n_llhttp__internal__n_error_46; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_schema_delim: s_n_llhttp__internal__n_url_schema_delim: { if (p == endp) { return s_n_llhttp__internal__n_url_schema_delim; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 10: { p++; goto s_n_llhttp__internal__n_error_45; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } case 13: { p++; goto s_n_llhttp__internal__n_error_45; } case ' ': { p++; goto s_n_llhttp__internal__n_error_45; } case '/': { p++; goto s_n_llhttp__internal__n_url_schema_delim_1; } default: { goto s_n_llhttp__internal__n_error_46; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_end_stub_schema: s_n_llhttp__internal__n_span_end_stub_schema: { if (p == endp) { return s_n_llhttp__internal__n_span_end_stub_schema; } p++; goto s_n_llhttp__internal__n_url_schema_delim; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_schema: s_n_llhttp__internal__n_url_schema: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_schema; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_1; } case 2: { p++; goto s_n_llhttp__internal__n_error_45; } case 3: { goto s_n_llhttp__internal__n_span_end_stub_schema; } case 4: { p++; goto s_n_llhttp__internal__n_url_schema; } default: { goto s_n_llhttp__internal__n_error_47; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_start: s_n_llhttp__internal__n_url_start: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_start; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_1; } case 2: { p++; goto s_n_llhttp__internal__n_error_45; } case 3: { goto s_n_llhttp__internal__n_span_start_stub_path_2; } case 4: { goto s_n_llhttp__internal__n_url_schema; } default: { goto s_n_llhttp__internal__n_error_48; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_url_1: s_n_llhttp__internal__n_span_start_llhttp__on_url_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_url_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_url; goto s_n_llhttp__internal__n_url_start; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_entry_normal: s_n_llhttp__internal__n_url_entry_normal: { if (p == endp) { return s_n_llhttp__internal__n_url_entry_normal; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_url: s_n_llhttp__internal__n_span_start_llhttp__on_url: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_url; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_url; goto s_n_llhttp__internal__n_url_server; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_entry_connect: s_n_llhttp__internal__n_url_entry_connect: { if (p == endp) { return s_n_llhttp__internal__n_url_entry_connect; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_error_1; } case 12: { p++; goto s_n_llhttp__internal__n_error_1; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_url; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_spaces_before_url: s_n_llhttp__internal__n_req_spaces_before_url: { if (p == endp) { return s_n_llhttp__internal__n_req_spaces_before_url; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_spaces_before_url; } default: { goto s_n_llhttp__internal__n_invoke_is_equal_method; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_first_space_before_url: s_n_llhttp__internal__n_req_first_space_before_url: { if (p == endp) { return s_n_llhttp__internal__n_req_first_space_before_url; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_spaces_before_url; } default: { goto s_n_llhttp__internal__n_error_49; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_2: s_n_llhttp__internal__n_start_req_2: { if (p == endp) { return s_n_llhttp__internal__n_start_req_2; } switch (*p) { case 'L': { p++; match = 19; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_3: s_n_llhttp__internal__n_start_req_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 36; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_1: s_n_llhttp__internal__n_start_req_1: { if (p == endp) { return s_n_llhttp__internal__n_start_req_1; } switch (*p) { case 'C': { p++; goto s_n_llhttp__internal__n_start_req_2; } case 'N': { p++; goto s_n_llhttp__internal__n_start_req_3; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_4: s_n_llhttp__internal__n_start_req_4: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_4; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 16; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_4; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_6: s_n_llhttp__internal__n_start_req_6: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_6; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 22; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_6; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_8: s_n_llhttp__internal__n_start_req_8: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_8; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_8; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_9: s_n_llhttp__internal__n_start_req_9: { if (p == endp) { return s_n_llhttp__internal__n_start_req_9; } switch (*p) { case 'Y': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_7: s_n_llhttp__internal__n_start_req_7: { if (p == endp) { return s_n_llhttp__internal__n_start_req_7; } switch (*p) { case 'N': { p++; goto s_n_llhttp__internal__n_start_req_8; } case 'P': { p++; goto s_n_llhttp__internal__n_start_req_9; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_5: s_n_llhttp__internal__n_start_req_5: { if (p == endp) { return s_n_llhttp__internal__n_start_req_5; } switch (*p) { case 'H': { p++; goto s_n_llhttp__internal__n_start_req_6; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_7; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_12: s_n_llhttp__internal__n_start_req_12: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_12; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_12; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_13: s_n_llhttp__internal__n_start_req_13: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_13; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 35; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_13; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_11: s_n_llhttp__internal__n_start_req_11: { if (p == endp) { return s_n_llhttp__internal__n_start_req_11; } switch (*p) { case 'L': { p++; goto s_n_llhttp__internal__n_start_req_12; } case 'S': { p++; goto s_n_llhttp__internal__n_start_req_13; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_10: s_n_llhttp__internal__n_start_req_10: { if (p == endp) { return s_n_llhttp__internal__n_start_req_10; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_start_req_11; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_14: s_n_llhttp__internal__n_start_req_14: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_14; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 45; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_14; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_17: s_n_llhttp__internal__n_start_req_17: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_17; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 41; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_17; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_16: s_n_llhttp__internal__n_start_req_16: { if (p == endp) { return s_n_llhttp__internal__n_start_req_16; } switch (*p) { case '_': { p++; goto s_n_llhttp__internal__n_start_req_17; } default: { match = 1; goto s_n_llhttp__internal__n_invoke_store_method_1; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_15: s_n_llhttp__internal__n_start_req_15: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_15; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_start_req_16; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_15; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_18: s_n_llhttp__internal__n_start_req_18: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_18; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_18; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_20: s_n_llhttp__internal__n_start_req_20: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_20; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 31; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_20; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_21: s_n_llhttp__internal__n_start_req_21: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_21; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_21; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_19: s_n_llhttp__internal__n_start_req_19: { if (p == endp) { return s_n_llhttp__internal__n_start_req_19; } switch (*p) { case 'I': { p++; goto s_n_llhttp__internal__n_start_req_20; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_21; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_23: s_n_llhttp__internal__n_start_req_23: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_23; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 24; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_23; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_24: s_n_llhttp__internal__n_start_req_24: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_24; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 23; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_24; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_26: s_n_llhttp__internal__n_start_req_26: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_26; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 21; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_26; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_28: s_n_llhttp__internal__n_start_req_28: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_28; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 30; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_28; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_29: s_n_llhttp__internal__n_start_req_29: { if (p == endp) { return s_n_llhttp__internal__n_start_req_29; } switch (*p) { case 'L': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_27: s_n_llhttp__internal__n_start_req_27: { if (p == endp) { return s_n_llhttp__internal__n_start_req_27; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_28; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_29; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_25: s_n_llhttp__internal__n_start_req_25: { if (p == endp) { return s_n_llhttp__internal__n_start_req_25; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_26; } case 'C': { p++; goto s_n_llhttp__internal__n_start_req_27; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_30: s_n_llhttp__internal__n_start_req_30: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_30; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 11; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_30; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_22: s_n_llhttp__internal__n_start_req_22: { if (p == endp) { return s_n_llhttp__internal__n_start_req_22; } switch (*p) { case '-': { p++; goto s_n_llhttp__internal__n_start_req_23; } case 'E': { p++; goto s_n_llhttp__internal__n_start_req_24; } case 'K': { p++; goto s_n_llhttp__internal__n_start_req_25; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_30; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_31: s_n_llhttp__internal__n_start_req_31: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_31; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 25; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_31; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_32: s_n_llhttp__internal__n_start_req_32: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_32; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_32; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_35: s_n_llhttp__internal__n_start_req_35: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_35; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 28; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_35; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_36: s_n_llhttp__internal__n_start_req_36: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_36; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 39; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_36; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_34: s_n_llhttp__internal__n_start_req_34: { if (p == endp) { return s_n_llhttp__internal__n_start_req_34; } switch (*p) { case 'T': { p++; goto s_n_llhttp__internal__n_start_req_35; } case 'U': { p++; goto s_n_llhttp__internal__n_start_req_36; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_37: s_n_llhttp__internal__n_start_req_37: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_37; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 38; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_37; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_38: s_n_llhttp__internal__n_start_req_38: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_38; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_38; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_42: s_n_llhttp__internal__n_start_req_42: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_42; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 12; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_42; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_43: s_n_llhttp__internal__n_start_req_43: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_43; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 13; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_43; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_41: s_n_llhttp__internal__n_start_req_41: { if (p == endp) { return s_n_llhttp__internal__n_start_req_41; } switch (*p) { case 'F': { p++; goto s_n_llhttp__internal__n_start_req_42; } case 'P': { p++; goto s_n_llhttp__internal__n_start_req_43; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_40: s_n_llhttp__internal__n_start_req_40: { if (p == endp) { return s_n_llhttp__internal__n_start_req_40; } switch (*p) { case 'P': { p++; goto s_n_llhttp__internal__n_start_req_41; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_39: s_n_llhttp__internal__n_start_req_39: { if (p == endp) { return s_n_llhttp__internal__n_start_req_39; } switch (*p) { case 'I': { p++; match = 34; goto s_n_llhttp__internal__n_invoke_store_method_1; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_40; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_45: s_n_llhttp__internal__n_start_req_45: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_45; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 29; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_45; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_44: s_n_llhttp__internal__n_start_req_44: { if (p == endp) { return s_n_llhttp__internal__n_start_req_44; } switch (*p) { case 'R': { p++; goto s_n_llhttp__internal__n_start_req_45; } case 'T': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_33: s_n_llhttp__internal__n_start_req_33: { if (p == endp) { return s_n_llhttp__internal__n_start_req_33; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_34; } case 'L': { p++; goto s_n_llhttp__internal__n_start_req_37; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_38; } case 'R': { p++; goto s_n_llhttp__internal__n_start_req_39; } case 'U': { p++; goto s_n_llhttp__internal__n_start_req_44; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_48: s_n_llhttp__internal__n_start_req_48: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_48; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 17; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_48; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_49: s_n_llhttp__internal__n_start_req_49: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_49; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 44; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_49; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_50: s_n_llhttp__internal__n_start_req_50: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_50; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 43; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_50; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_51: s_n_llhttp__internal__n_start_req_51: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_51; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 20; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_51; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_47: s_n_llhttp__internal__n_start_req_47: { if (p == endp) { return s_n_llhttp__internal__n_start_req_47; } switch (*p) { case 'B': { p++; goto s_n_llhttp__internal__n_start_req_48; } case 'C': { p++; goto s_n_llhttp__internal__n_start_req_49; } case 'D': { p++; goto s_n_llhttp__internal__n_start_req_50; } case 'P': { p++; goto s_n_llhttp__internal__n_start_req_51; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_46: s_n_llhttp__internal__n_start_req_46: { if (p == endp) { return s_n_llhttp__internal__n_start_req_46; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_start_req_47; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_54: s_n_llhttp__internal__n_start_req_54: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_54; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 14; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_54; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_56: s_n_llhttp__internal__n_start_req_56: { if (p == endp) { return s_n_llhttp__internal__n_start_req_56; } switch (*p) { case 'P': { p++; match = 37; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_57: s_n_llhttp__internal__n_start_req_57: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_57; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 42; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_57; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_55: s_n_llhttp__internal__n_start_req_55: { if (p == endp) { return s_n_llhttp__internal__n_start_req_55; } switch (*p) { case 'U': { p++; goto s_n_llhttp__internal__n_start_req_56; } case '_': { p++; goto s_n_llhttp__internal__n_start_req_57; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_53: s_n_llhttp__internal__n_start_req_53: { if (p == endp) { return s_n_llhttp__internal__n_start_req_53; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_54; } case 'T': { p++; goto s_n_llhttp__internal__n_start_req_55; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_58: s_n_llhttp__internal__n_start_req_58: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_58; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 33; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_58; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_59: s_n_llhttp__internal__n_start_req_59: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_59; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 26; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_59; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_52: s_n_llhttp__internal__n_start_req_52: { if (p == endp) { return s_n_llhttp__internal__n_start_req_52; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_start_req_53; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_58; } case 'U': { p++; goto s_n_llhttp__internal__n_start_req_59; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_61: s_n_llhttp__internal__n_start_req_61: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_61; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 40; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_61; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_62: s_n_llhttp__internal__n_start_req_62: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_62; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_62; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_60: s_n_llhttp__internal__n_start_req_60: { if (p == endp) { return s_n_llhttp__internal__n_start_req_60; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_start_req_61; } case 'R': { p++; goto s_n_llhttp__internal__n_start_req_62; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_65: s_n_llhttp__internal__n_start_req_65: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_65; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 18; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_65; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_67: s_n_llhttp__internal__n_start_req_67: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_67; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 32; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_67; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_68: s_n_llhttp__internal__n_start_req_68: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_68; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 15; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_68; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_66: s_n_llhttp__internal__n_start_req_66: { if (p == endp) { return s_n_llhttp__internal__n_start_req_66; } switch (*p) { case 'I': { p++; goto s_n_llhttp__internal__n_start_req_67; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_68; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_69: s_n_llhttp__internal__n_start_req_69: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_69; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 27; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_69; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_64: s_n_llhttp__internal__n_start_req_64: { if (p == endp) { return s_n_llhttp__internal__n_start_req_64; } switch (*p) { case 'B': { p++; goto s_n_llhttp__internal__n_start_req_65; } case 'L': { p++; goto s_n_llhttp__internal__n_start_req_66; } case 'S': { p++; goto s_n_llhttp__internal__n_start_req_69; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_63: s_n_llhttp__internal__n_start_req_63: { if (p == endp) { return s_n_llhttp__internal__n_start_req_63; } switch (*p) { case 'N': { p++; goto s_n_llhttp__internal__n_start_req_64; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req: s_n_llhttp__internal__n_start_req: { if (p == endp) { return s_n_llhttp__internal__n_start_req; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_1; } case 'B': { p++; goto s_n_llhttp__internal__n_start_req_4; } case 'C': { p++; goto s_n_llhttp__internal__n_start_req_5; } case 'D': { p++; goto s_n_llhttp__internal__n_start_req_10; } case 'F': { p++; goto s_n_llhttp__internal__n_start_req_14; } case 'G': { p++; goto s_n_llhttp__internal__n_start_req_15; } case 'H': { p++; goto s_n_llhttp__internal__n_start_req_18; } case 'L': { p++; goto s_n_llhttp__internal__n_start_req_19; } case 'M': { p++; goto s_n_llhttp__internal__n_start_req_22; } case 'N': { p++; goto s_n_llhttp__internal__n_start_req_31; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_32; } case 'P': { p++; goto s_n_llhttp__internal__n_start_req_33; } case 'R': { p++; goto s_n_llhttp__internal__n_start_req_46; } case 'S': { p++; goto s_n_llhttp__internal__n_start_req_52; } case 'T': { p++; goto s_n_llhttp__internal__n_start_req_60; } case 'U': { p++; goto s_n_llhttp__internal__n_start_req_63; } default: { goto s_n_llhttp__internal__n_error_58; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: { switch (llhttp__on_status_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_line_almost_done: s_n_llhttp__internal__n_res_line_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_res_line_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_status: s_n_llhttp__internal__n_res_status: { if (p == endp) { return s_n_llhttp__internal__n_res_status; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_status; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1; } default: { p++; goto s_n_llhttp__internal__n_res_status; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_status: s_n_llhttp__internal__n_span_start_llhttp__on_status: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_status; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_status; goto s_n_llhttp__internal__n_res_status; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_status_start: s_n_llhttp__internal__n_res_status_start: { if (p == endp) { return s_n_llhttp__internal__n_res_status_start; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; } case 13: { p++; goto s_n_llhttp__internal__n_res_line_almost_done; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_status; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_status_code_otherwise: s_n_llhttp__internal__n_res_status_code_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_res_status_code_otherwise; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_res_status_start; } case 13: { goto s_n_llhttp__internal__n_res_status_start; } case ' ': { p++; goto s_n_llhttp__internal__n_res_status_start; } default: { goto s_n_llhttp__internal__n_error_52; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_status_code: s_n_llhttp__internal__n_res_status_code: { if (p == endp) { return s_n_llhttp__internal__n_res_status_code; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } default: { goto s_n_llhttp__internal__n_res_status_code_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_http_end: s_n_llhttp__internal__n_res_http_end: { if (p == endp) { return s_n_llhttp__internal__n_res_http_end; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_invoke_update_status_code; } default: { goto s_n_llhttp__internal__n_error_53; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_http_minor: s_n_llhttp__internal__n_res_http_minor: { if (p == endp) { return s_n_llhttp__internal__n_res_http_minor; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } default: { goto s_n_llhttp__internal__n_error_54; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_http_dot: s_n_llhttp__internal__n_res_http_dot: { if (p == endp) { return s_n_llhttp__internal__n_res_http_dot; } switch (*p) { case '.': { p++; goto s_n_llhttp__internal__n_res_http_minor; } default: { goto s_n_llhttp__internal__n_error_55; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_http_major: s_n_llhttp__internal__n_res_http_major: { if (p == endp) { return s_n_llhttp__internal__n_res_http_major; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } default: { goto s_n_llhttp__internal__n_error_56; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_res: s_n_llhttp__internal__n_start_res: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_res; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_res_http_major; } case kMatchPause: { return s_n_llhttp__internal__n_start_res; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_59; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_or_res_method_2: s_n_llhttp__internal__n_req_or_res_method_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_2; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_method; } case kMatchPause: { return s_n_llhttp__internal__n_req_or_res_method_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_57; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_or_res_method_3: s_n_llhttp__internal__n_req_or_res_method_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_type_1; } case kMatchPause: { return s_n_llhttp__internal__n_req_or_res_method_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_57; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_or_res_method_1: s_n_llhttp__internal__n_req_or_res_method_1: { if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_1; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_req_or_res_method_2; } case 'T': { p++; goto s_n_llhttp__internal__n_req_or_res_method_3; } default: { goto s_n_llhttp__internal__n_error_57; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_or_res_method: s_n_llhttp__internal__n_req_or_res_method: { if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method; } switch (*p) { case 'H': { p++; goto s_n_llhttp__internal__n_req_or_res_method_1; } default: { goto s_n_llhttp__internal__n_error_57; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_or_res: s_n_llhttp__internal__n_start_req_or_res: { if (p == endp) { return s_n_llhttp__internal__n_start_req_or_res; } switch (*p) { case 'H': { goto s_n_llhttp__internal__n_req_or_res_method; } default: { goto s_n_llhttp__internal__n_invoke_update_type_2; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_load_type: s_n_llhttp__internal__n_invoke_load_type: { switch (llhttp__internal__c_load_type(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_start_req; case 2: goto s_n_llhttp__internal__n_start_res; default: goto s_n_llhttp__internal__n_start_req_or_res; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start: s_n_llhttp__internal__n_start: { if (p == endp) { return s_n_llhttp__internal__n_start; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_start; } case 13: { p++; goto s_n_llhttp__internal__n_start; } default: { goto s_n_llhttp__internal__n_invoke_update_finish; } } /* UNREACHABLE */; abort(); } default: /* UNREACHABLE */ abort(); } s_n_llhttp__internal__n_error_1: { state->error = 0x7; state->reason = "Invalid characters in url (strict mode)"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_45: { state->error = 0x7; state->reason = "Invalid characters in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_finish_2: { switch (llhttp__internal__c_update_finish_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_start; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_4: { state->error = 0x5; state->reason = "Data after `Connection: close`"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_lenient_flags: { switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_update_finish_2; default: goto s_n_llhttp__internal__n_closed; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_finish_1: { switch (llhttp__internal__c_update_finish_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_test_lenient_flags; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_5: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_14: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_7: { state->error = 0x15; state->reason = "on_chunk_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_17: { state->error = 0x14; state->reason = "`on_chunk_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: { switch (llhttp__on_chunk_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; case 21: goto s_n_llhttp__internal__n_pause_7; default: goto s_n_llhttp__internal__n_error_17; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_16: { state->error = 0x4; state->reason = "Content-Length can't be present with Transfer-Encoding"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_2: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_5: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_pause_1; case 21: goto s_n_llhttp__internal__n_pause_2; default: goto s_n_llhttp__internal__n_error_5; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_12: { state->error = 0xc; state->reason = "Chunk size overflow"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_3: { state->error = 0x15; state->reason = "on_chunk_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_7: { state->error = 0x14; state->reason = "`on_chunk_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: { switch (llhttp__on_chunk_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_update_content_length; case 21: goto s_n_llhttp__internal__n_pause_3; default: goto s_n_llhttp__internal__n_error_7; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_8: { state->error = 0x2; state->reason = "Expected CRLF after chunk"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_body: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_body(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done; return s_error; } goto s_n_llhttp__internal__n_chunk_data_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags: { switch (llhttp__internal__c_or_flags(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_4: { state->error = 0x15; state->reason = "on_chunk_header pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_6: { state->error = 0x13; state->reason = "`on_chunk_header` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: { switch (llhttp__on_chunk_header(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_is_equal_content_length; case 21: goto s_n_llhttp__internal__n_pause_4; default: goto s_n_llhttp__internal__n_error_6; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_9: { state->error = 0x2; state->reason = "Expected LF after chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_10: { state->error = 0x2; state->reason = "Invalid character in chunk parameters"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_11: { state->error = 0xc; state->reason = "Invalid character in chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_mul_add_content_length: { switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_error_12; default: goto s_n_llhttp__internal__n_chunk_size; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_13: { state->error = 0xc; state->reason = "Invalid character in chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_body_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_body(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_finish_3: { switch (llhttp__internal__c_update_finish_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_15: { state->error = 0xf; state->reason = "Request has invalid `Transfer-Encoding`"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_3: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; case 21: goto s_n_llhttp__internal__n_pause; default: goto s_n_llhttp__internal__n_error_3; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_1: { switch (llhttp__internal__c_or_flags_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_2: { switch (llhttp__internal__c_or_flags_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_upgrade: { switch (llhttp__internal__c_update_upgrade(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_or_flags_2; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_6: { state->error = 0x15; state->reason = "Paused by on_headers_complete"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_2: { state->error = 0x11; state->reason = "User callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: { switch (llhttp__on_headers_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; case 1: goto s_n_llhttp__internal__n_invoke_or_flags_1; case 2: goto s_n_llhttp__internal__n_invoke_update_upgrade; case 21: goto s_n_llhttp__internal__n_pause_6; default: goto s_n_llhttp__internal__n_error_2; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: { switch (llhttp__before_headers_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_lenient_flags_1: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_error_16; default: goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_flags_1: { switch (llhttp__internal__c_test_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1; default: goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_flags: { switch (llhttp__internal__c_test_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1; default: goto s_n_llhttp__internal__n_invoke_test_flags_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_18: { state->error = 0x2; state->reason = "Expected LF after headers"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_field: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_19; return s_error; } p++; goto s_n_llhttp__internal__n_error_19; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_lenient_flags_2: { switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_field_colon_discard_ws; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_20: { state->error = 0xb; state->reason = "Empty Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_3: { switch (llhttp__internal__c_or_flags_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_4: { switch (llhttp__internal__c_or_flags_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_5: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_6: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state_1: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_3; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_4; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_5; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_6; default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 2: goto s_n_llhttp__internal__n_error_20; default: goto s_n_llhttp__internal__n_invoke_load_header_state_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_21: { state->error = 0x2; state->reason = "Expected LF after CR"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_1: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_7: { switch (llhttp__internal__c_or_flags_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_8: { switch (llhttp__internal__c_or_flags_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_9: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_10: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state_3: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_7; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_8; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_9; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_10; default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_22: { state->error = 0x3; state->reason = "Missing expected LF after header value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } goto s_n_llhttp__internal__n_header_value_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_header_value_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_header_value_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_23: { state->error = 0xa; state->reason = "Invalid header value char"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_lenient_flags_3: { switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_lenient; default: goto s_n_llhttp__internal__n_error_23; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_3: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_11: { switch (llhttp__internal__c_or_flags_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_3; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_12: { switch (llhttp__internal__c_or_flags_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_3; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_13: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_3; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_14: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state_4: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_11; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_12; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_13; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_14; default: goto s_n_llhttp__internal__n_header_value_connection; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_4: { switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_token; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_2: { switch (llhttp__internal__c_update_header_state_2(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_5: { switch (llhttp__internal__c_update_header_state_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_6: { switch (llhttp__internal__c_update_header_state_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_25; return s_error; } goto s_n_llhttp__internal__n_error_25; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; default: goto s_n_llhttp__internal__n_header_value_content_length; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_15: { switch (llhttp__internal__c_or_flags_15(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_otherwise; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_26; return s_error; } goto s_n_llhttp__internal__n_error_26; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_24: { state->error = 0x4; state->reason = "Duplicate Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_flags_2: { switch (llhttp__internal__c_test_flags_2(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_header_value_content_length; default: goto s_n_llhttp__internal__n_error_24; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_7: { switch (llhttp__internal__c_update_header_state_7(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_otherwise; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_8: { switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_and_flags: { switch (llhttp__internal__c_and_flags(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_te_chunked; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_16: { switch (llhttp__internal__c_or_flags_16(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_and_flags; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_17: { switch (llhttp__internal__c_or_flags_17(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_8; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state_2: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_connection; case 2: goto s_n_llhttp__internal__n_invoke_test_flags_2; case 3: goto s_n_llhttp__internal__n_invoke_or_flags_16; case 4: goto s_n_llhttp__internal__n_invoke_or_flags_17; default: goto s_n_llhttp__internal__n_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_27: { state->error = 0xa; state->reason = "Invalid header token"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_9: { switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_general; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_header_state: { switch (llhttp__internal__c_store_header_state(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_header_field_colon; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_10: { switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_general; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: { switch (llhttp__on_url_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_http_minor: { switch (llhttp__internal__c_update_http_minor(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_http_major: { switch (llhttp__internal__c_update_http_major(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_http_minor; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_28: { state->error = 0x7; state->reason = "Expected CRLF"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_31: { state->error = 0x17; state->reason = "Pause on PRI/Upgrade"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_32: { state->error = 0x9; state->reason = "Expected HTTP/2 Connection Preface"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_30: { state->error = 0x9; state->reason = "Expected CRLF after version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_method_1: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 34: goto s_n_llhttp__internal__n_req_pri_upgrade; default: goto s_n_llhttp__internal__n_req_http_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_http_minor: { switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_invoke_load_method_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_33: { state->error = 0x9; state->reason = "Invalid minor version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_34: { state->error = 0x9; state->reason = "Expected dot"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_http_major: { switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_req_http_dot; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_35: { state->error = 0x9; state->reason = "Invalid major version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_29: { state->error = 0x8; state->reason = "Invalid method for HTTP/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_method: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_req_http_major; case 1: goto s_n_llhttp__internal__n_req_http_major; case 2: goto s_n_llhttp__internal__n_req_http_major; case 3: goto s_n_llhttp__internal__n_req_http_major; case 4: goto s_n_llhttp__internal__n_req_http_major; case 5: goto s_n_llhttp__internal__n_req_http_major; case 6: goto s_n_llhttp__internal__n_req_http_major; case 7: goto s_n_llhttp__internal__n_req_http_major; case 8: goto s_n_llhttp__internal__n_req_http_major; case 9: goto s_n_llhttp__internal__n_req_http_major; case 10: goto s_n_llhttp__internal__n_req_http_major; case 11: goto s_n_llhttp__internal__n_req_http_major; case 12: goto s_n_llhttp__internal__n_req_http_major; case 13: goto s_n_llhttp__internal__n_req_http_major; case 14: goto s_n_llhttp__internal__n_req_http_major; case 15: goto s_n_llhttp__internal__n_req_http_major; case 16: goto s_n_llhttp__internal__n_req_http_major; case 17: goto s_n_llhttp__internal__n_req_http_major; case 18: goto s_n_llhttp__internal__n_req_http_major; case 19: goto s_n_llhttp__internal__n_req_http_major; case 20: goto s_n_llhttp__internal__n_req_http_major; case 21: goto s_n_llhttp__internal__n_req_http_major; case 22: goto s_n_llhttp__internal__n_req_http_major; case 23: goto s_n_llhttp__internal__n_req_http_major; case 24: goto s_n_llhttp__internal__n_req_http_major; case 25: goto s_n_llhttp__internal__n_req_http_major; case 26: goto s_n_llhttp__internal__n_req_http_major; case 27: goto s_n_llhttp__internal__n_req_http_major; case 28: goto s_n_llhttp__internal__n_req_http_major; case 29: goto s_n_llhttp__internal__n_req_http_major; case 30: goto s_n_llhttp__internal__n_req_http_major; case 31: goto s_n_llhttp__internal__n_req_http_major; case 32: goto s_n_llhttp__internal__n_req_http_major; case 33: goto s_n_llhttp__internal__n_req_http_major; case 34: goto s_n_llhttp__internal__n_req_http_major; default: goto s_n_llhttp__internal__n_error_29; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_38: { state->error = 0x8; state->reason = "Expected HTTP/"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_36: { state->error = 0x8; state->reason = "Expected SOURCE method for ICE/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_method_2: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 33: goto s_n_llhttp__internal__n_req_http_major; default: goto s_n_llhttp__internal__n_error_36; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_37: { state->error = 0x8; state->reason = "Invalid method for RTSP/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_method_3: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_req_http_major; case 3: goto s_n_llhttp__internal__n_req_http_major; case 6: goto s_n_llhttp__internal__n_req_http_major; case 35: goto s_n_llhttp__internal__n_req_http_major; case 36: goto s_n_llhttp__internal__n_req_http_major; case 37: goto s_n_llhttp__internal__n_req_http_major; case 38: goto s_n_llhttp__internal__n_req_http_major; case 39: goto s_n_llhttp__internal__n_req_http_major; case 40: goto s_n_llhttp__internal__n_req_http_major; case 41: goto s_n_llhttp__internal__n_req_http_major; case 42: goto s_n_llhttp__internal__n_req_http_major; case 43: goto s_n_llhttp__internal__n_req_http_major; case 44: goto s_n_llhttp__internal__n_req_http_major; case 45: goto s_n_llhttp__internal__n_req_http_major; default: goto s_n_llhttp__internal__n_error_37; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: { switch (llhttp__on_url_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_req_http_start; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_6: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_7: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_8: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_39: { state->error = 0x7; state->reason = "Invalid char in url fragment start"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_9: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_10: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_11: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_40: { state->error = 0x7; state->reason = "Invalid char in url query"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_41: { state->error = 0x7; state->reason = "Invalid char in url path"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_12: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_13: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_14: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_42: { state->error = 0x7; state->reason = "Double @ in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_43: { state->error = 0x7; state->reason = "Unexpected char in url server"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_44: { state->error = 0x7; state->reason = "Unexpected char in url server"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_46: { state->error = 0x7; state->reason = "Unexpected char in url schema"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_47: { state->error = 0x7; state->reason = "Unexpected char in url schema"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_48: { state->error = 0x7; state->reason = "Unexpected start char in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_is_equal_method: { switch (llhttp__internal__c_is_equal_method(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_url_entry_normal; default: goto s_n_llhttp__internal__n_url_entry_connect; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_49: { state->error = 0x6; state->reason = "Expected space after method"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_method_1: { switch (llhttp__internal__c_store_method(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_req_first_space_before_url; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_58: { state->error = 0x6; state->reason = "Invalid method encountered"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_50: { state->error = 0xd; state->reason = "Response overflow"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_mul_add_status_code: { switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_error_50; default: goto s_n_llhttp__internal__n_res_status_code; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_51: { state->error = 0x2; state->reason = "Expected LF after CR"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_status: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_status(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_status_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_status(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_res_line_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_52: { state->error = 0xd; state->reason = "Invalid response status"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_status_code: { switch (llhttp__internal__c_update_status_code(state, p, endp)) { default: goto s_n_llhttp__internal__n_res_status_code; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_53: { state->error = 0x9; state->reason = "Expected space after version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_http_minor_1: { switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_res_http_end; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_54: { state->error = 0x9; state->reason = "Invalid minor version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_55: { state->error = 0x9; state->reason = "Expected dot"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_http_major_1: { switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_res_http_dot; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_56: { state->error = 0x9; state->reason = "Invalid major version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_59: { state->error = 0x8; state->reason = "Expected HTTP/"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_type: { switch (llhttp__internal__c_update_type(state, p, endp)) { default: goto s_n_llhttp__internal__n_req_first_space_before_url; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_method: { switch (llhttp__internal__c_store_method(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_invoke_update_type; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_57: { state->error = 0x8; state->reason = "Invalid word encountered"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_type_1: { switch (llhttp__internal__c_update_type_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_res_http_major; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_type_2: { switch (llhttp__internal__c_update_type(state, p, endp)) { default: goto s_n_llhttp__internal__n_start_req; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_8: { state->error = 0x15; state->reason = "on_message_begin pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error: { state->error = 0x10; state->reason = "`on_message_begin` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: { switch (llhttp__on_message_begin(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_type; case 21: goto s_n_llhttp__internal__n_pause_8; default: goto s_n_llhttp__internal__n_error; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_finish: { switch (llhttp__internal__c_update_finish(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin; } /* UNREACHABLE */; abort(); } } int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) { llparse_state_t next; /* check lingering errors */ if (state->error != 0) { return state->error; } /* restart spans */ if (state->_span_pos0 != NULL) { state->_span_pos0 = (void*) p; } next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); if (next == s_error) { return state->error; } state->_current = (void*) (intptr_t) next; /* execute spans */ if (state->_span_pos0 != NULL) { int error; error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); if (error != 0) { state->error = error; state->error_pos = endp; return error; } } return 0; } #else /* !LLHTTP_STRICT_MODE */ #include #include #include #ifdef __SSE4_2__ #ifdef _MSC_VER #include #else /* !_MSC_VER */ #include #endif /* _MSC_VER */ #endif /* __SSE4_2__ */ #ifdef _MSC_VER #define ALIGN(n) _declspec(align(n)) #else /* !_MSC_VER */ #define ALIGN(n) __attribute__((aligned(n))) #endif /* _MSC_VER */ #include "llhttp.h" typedef int (*llhttp__internal__span_cb)( llhttp__internal_t*, const char*, const char*); #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob0[] = { 0x9, 0x9, 0xc, 0xc, '!', '"', '$', '>', '@', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0 }; #endif /* __SSE4_2__ */ static const unsigned char llparse_blob1[] = { 'o', 'n' }; static const unsigned char llparse_blob2[] = { 'e', 'c', 't', 'i', 'o', 'n' }; static const unsigned char llparse_blob3[] = { 'l', 'o', 's', 'e' }; static const unsigned char llparse_blob4[] = { 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' }; static const unsigned char llparse_blob5[] = { 'p', 'g', 'r', 'a', 'd', 'e' }; static const unsigned char llparse_blob6[] = { 'c', 'h', 'u', 'n', 'k', 'e', 'd' }; #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob7[] = { 0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; #endif /* __SSE4_2__ */ #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob8[] = { ' ', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', 'Z', '^', 'z', '|', '|' }; #endif /* __SSE4_2__ */ #ifdef __SSE4_2__ static const unsigned char ALIGN(16) llparse_blob9[] = { '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; #endif /* __SSE4_2__ */ static const unsigned char llparse_blob10[] = { 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' }; static const unsigned char llparse_blob11[] = { 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n' }; static const unsigned char llparse_blob12[] = { 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g' }; static const unsigned char llparse_blob13[] = { 'p', 'g', 'r', 'a', 'd', 'e' }; static const unsigned char llparse_blob14[] = { 0xd, 0xa }; static const unsigned char llparse_blob15[] = { 'T', 'T', 'P', '/' }; static const unsigned char llparse_blob16[] = { 0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa }; static const unsigned char llparse_blob17[] = { 'C', 'E', '/' }; static const unsigned char llparse_blob18[] = { 'T', 'S', 'P', '/' }; static const unsigned char llparse_blob19[] = { 'N', 'O', 'U', 'N', 'C', 'E' }; static const unsigned char llparse_blob20[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob21[] = { 'E', 'C', 'K', 'O', 'U', 'T' }; static const unsigned char llparse_blob22[] = { 'N', 'E', 'C', 'T' }; static const unsigned char llparse_blob23[] = { 'E', 'T', 'E' }; static const unsigned char llparse_blob24[] = { 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob25[] = { 'L', 'U', 'S', 'H' }; static const unsigned char llparse_blob26[] = { 'E', 'T' }; static const unsigned char llparse_blob27[] = { 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' }; static const unsigned char llparse_blob28[] = { 'E', 'A', 'D' }; static const unsigned char llparse_blob29[] = { 'N', 'K' }; static const unsigned char llparse_blob30[] = { 'C', 'K' }; static const unsigned char llparse_blob31[] = { 'S', 'E', 'A', 'R', 'C', 'H' }; static const unsigned char llparse_blob32[] = { 'R', 'G', 'E' }; static const unsigned char llparse_blob33[] = { 'C', 'T', 'I', 'V', 'I', 'T', 'Y' }; static const unsigned char llparse_blob34[] = { 'L', 'E', 'N', 'D', 'A', 'R' }; static const unsigned char llparse_blob35[] = { 'V', 'E' }; static const unsigned char llparse_blob36[] = { 'O', 'T', 'I', 'F', 'Y' }; static const unsigned char llparse_blob37[] = { 'P', 'T', 'I', 'O', 'N', 'S' }; static const unsigned char llparse_blob38[] = { 'C', 'H' }; static const unsigned char llparse_blob39[] = { 'S', 'E' }; static const unsigned char llparse_blob40[] = { 'A', 'Y' }; static const unsigned char llparse_blob41[] = { 'S', 'T' }; static const unsigned char llparse_blob42[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob43[] = { 'A', 'T', 'C', 'H' }; static const unsigned char llparse_blob44[] = { 'G', 'E' }; static const unsigned char llparse_blob45[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob46[] = { 'O', 'R', 'D' }; static const unsigned char llparse_blob47[] = { 'I', 'R', 'E', 'C', 'T' }; static const unsigned char llparse_blob48[] = { 'O', 'R', 'T' }; static const unsigned char llparse_blob49[] = { 'R', 'C', 'H' }; static const unsigned char llparse_blob50[] = { 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' }; static const unsigned char llparse_blob51[] = { 'U', 'R', 'C', 'E' }; static const unsigned char llparse_blob52[] = { 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob53[] = { 'A', 'R', 'D', 'O', 'W', 'N' }; static const unsigned char llparse_blob54[] = { 'A', 'C', 'E' }; static const unsigned char llparse_blob55[] = { 'I', 'N', 'D' }; static const unsigned char llparse_blob56[] = { 'N', 'K' }; static const unsigned char llparse_blob57[] = { 'C', 'K' }; static const unsigned char llparse_blob58[] = { 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; static const unsigned char llparse_blob59[] = { 'H', 'T', 'T', 'P', '/' }; static const unsigned char llparse_blob60[] = { 'A', 'D' }; static const unsigned char llparse_blob61[] = { 'T', 'P', '/' }; enum llparse_match_status_e { kMatchComplete, kMatchPause, kMatchMismatch }; typedef enum llparse_match_status_e llparse_match_status_t; struct llparse_match_s { llparse_match_status_t status; const unsigned char* current; }; typedef struct llparse_match_s llparse_match_t; static llparse_match_t llparse__match_sequence_to_lower( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p)); if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } static llparse_match_t llparse__match_sequence_to_lower_unsafe( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = ((*p) | 0x20); if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } static llparse_match_t llparse__match_sequence_id( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp, const unsigned char* seq, uint32_t seq_len) { uint32_t index; llparse_match_t res; index = s->_index; for (; p != endp; p++) { unsigned char current; current = *p; if (current == seq[index]) { if (++index == seq_len) { res.status = kMatchComplete; goto reset; } } else { res.status = kMatchMismatch; goto reset; } } s->_index = index; res.status = kMatchPause; res.current = p; return res; reset: s->_index = 0; res.current = p; return res; } enum llparse_state_e { s_error, s_n_llhttp__internal__n_closed, s_n_llhttp__internal__n_invoke_llhttp__after_message_complete, s_n_llhttp__internal__n_pause_1, s_n_llhttp__internal__n_invoke_is_equal_upgrade, s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2, s_n_llhttp__internal__n_chunk_data_almost_done_skip, s_n_llhttp__internal__n_chunk_data_almost_done, s_n_llhttp__internal__n_consume_content_length, s_n_llhttp__internal__n_span_start_llhttp__on_body, s_n_llhttp__internal__n_invoke_is_equal_content_length, s_n_llhttp__internal__n_chunk_size_almost_done, s_n_llhttp__internal__n_chunk_parameters, s_n_llhttp__internal__n_chunk_size_otherwise, s_n_llhttp__internal__n_chunk_size, s_n_llhttp__internal__n_chunk_size_digit, s_n_llhttp__internal__n_invoke_update_content_length, s_n_llhttp__internal__n_consume_content_length_1, s_n_llhttp__internal__n_span_start_llhttp__on_body_1, s_n_llhttp__internal__n_eof, s_n_llhttp__internal__n_span_start_llhttp__on_body_2, s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete, s_n_llhttp__internal__n_headers_almost_done, s_n_llhttp__internal__n_header_field_colon_discard_ws, s_n_llhttp__internal__n_error_14, s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete, s_n_llhttp__internal__n_span_start_llhttp__on_header_value, s_n_llhttp__internal__n_header_value_discard_lws, s_n_llhttp__internal__n_header_value_discard_ws_almost_done, s_n_llhttp__internal__n_header_value_lws, s_n_llhttp__internal__n_header_value_almost_done, s_n_llhttp__internal__n_header_value_lenient, s_n_llhttp__internal__n_header_value_otherwise, s_n_llhttp__internal__n_header_value_connection_token, s_n_llhttp__internal__n_header_value_connection_ws, s_n_llhttp__internal__n_header_value_connection_1, s_n_llhttp__internal__n_header_value_connection_2, s_n_llhttp__internal__n_header_value_connection_3, s_n_llhttp__internal__n_header_value_connection, s_n_llhttp__internal__n_error_19, s_n_llhttp__internal__n_error_20, s_n_llhttp__internal__n_header_value_content_length_ws, s_n_llhttp__internal__n_header_value_content_length, s_n_llhttp__internal__n_header_value_te_chunked_last, s_n_llhttp__internal__n_header_value_te_token_ows, s_n_llhttp__internal__n_header_value, s_n_llhttp__internal__n_header_value_te_token, s_n_llhttp__internal__n_header_value_te_chunked, s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1, s_n_llhttp__internal__n_header_value_discard_ws, s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete, s_n_llhttp__internal__n_header_field_general_otherwise, s_n_llhttp__internal__n_header_field_general, s_n_llhttp__internal__n_header_field_colon, s_n_llhttp__internal__n_header_field_3, s_n_llhttp__internal__n_header_field_4, s_n_llhttp__internal__n_header_field_2, s_n_llhttp__internal__n_header_field_1, s_n_llhttp__internal__n_header_field_5, s_n_llhttp__internal__n_header_field_6, s_n_llhttp__internal__n_header_field_7, s_n_llhttp__internal__n_header_field, s_n_llhttp__internal__n_span_start_llhttp__on_header_field, s_n_llhttp__internal__n_header_field_start, s_n_llhttp__internal__n_url_skip_to_http09, s_n_llhttp__internal__n_url_skip_lf_to_http09, s_n_llhttp__internal__n_req_pri_upgrade, s_n_llhttp__internal__n_req_http_complete_1, s_n_llhttp__internal__n_req_http_complete, s_n_llhttp__internal__n_req_http_minor, s_n_llhttp__internal__n_req_http_dot, s_n_llhttp__internal__n_req_http_major, s_n_llhttp__internal__n_req_http_start_1, s_n_llhttp__internal__n_req_http_start_2, s_n_llhttp__internal__n_req_http_start_3, s_n_llhttp__internal__n_req_http_start, s_n_llhttp__internal__n_url_skip_to_http, s_n_llhttp__internal__n_url_fragment, s_n_llhttp__internal__n_span_end_stub_query_3, s_n_llhttp__internal__n_url_query, s_n_llhttp__internal__n_url_query_or_fragment, s_n_llhttp__internal__n_url_path, s_n_llhttp__internal__n_span_start_stub_path_2, s_n_llhttp__internal__n_span_start_stub_path, s_n_llhttp__internal__n_span_start_stub_path_1, s_n_llhttp__internal__n_url_server_with_at, s_n_llhttp__internal__n_url_server, s_n_llhttp__internal__n_url_schema_delim_1, s_n_llhttp__internal__n_url_schema_delim, s_n_llhttp__internal__n_span_end_stub_schema, s_n_llhttp__internal__n_url_schema, s_n_llhttp__internal__n_url_start, s_n_llhttp__internal__n_span_start_llhttp__on_url_1, s_n_llhttp__internal__n_span_start_llhttp__on_url, s_n_llhttp__internal__n_req_spaces_before_url, s_n_llhttp__internal__n_req_first_space_before_url, s_n_llhttp__internal__n_start_req_2, s_n_llhttp__internal__n_start_req_3, s_n_llhttp__internal__n_start_req_1, s_n_llhttp__internal__n_start_req_4, s_n_llhttp__internal__n_start_req_6, s_n_llhttp__internal__n_start_req_8, s_n_llhttp__internal__n_start_req_9, s_n_llhttp__internal__n_start_req_7, s_n_llhttp__internal__n_start_req_5, s_n_llhttp__internal__n_start_req_12, s_n_llhttp__internal__n_start_req_13, s_n_llhttp__internal__n_start_req_11, s_n_llhttp__internal__n_start_req_10, s_n_llhttp__internal__n_start_req_14, s_n_llhttp__internal__n_start_req_17, s_n_llhttp__internal__n_start_req_16, s_n_llhttp__internal__n_start_req_15, s_n_llhttp__internal__n_start_req_18, s_n_llhttp__internal__n_start_req_20, s_n_llhttp__internal__n_start_req_21, s_n_llhttp__internal__n_start_req_19, s_n_llhttp__internal__n_start_req_23, s_n_llhttp__internal__n_start_req_24, s_n_llhttp__internal__n_start_req_26, s_n_llhttp__internal__n_start_req_28, s_n_llhttp__internal__n_start_req_29, s_n_llhttp__internal__n_start_req_27, s_n_llhttp__internal__n_start_req_25, s_n_llhttp__internal__n_start_req_30, s_n_llhttp__internal__n_start_req_22, s_n_llhttp__internal__n_start_req_31, s_n_llhttp__internal__n_start_req_32, s_n_llhttp__internal__n_start_req_35, s_n_llhttp__internal__n_start_req_36, s_n_llhttp__internal__n_start_req_34, s_n_llhttp__internal__n_start_req_37, s_n_llhttp__internal__n_start_req_38, s_n_llhttp__internal__n_start_req_42, s_n_llhttp__internal__n_start_req_43, s_n_llhttp__internal__n_start_req_41, s_n_llhttp__internal__n_start_req_40, s_n_llhttp__internal__n_start_req_39, s_n_llhttp__internal__n_start_req_45, s_n_llhttp__internal__n_start_req_44, s_n_llhttp__internal__n_start_req_33, s_n_llhttp__internal__n_start_req_48, s_n_llhttp__internal__n_start_req_49, s_n_llhttp__internal__n_start_req_50, s_n_llhttp__internal__n_start_req_51, s_n_llhttp__internal__n_start_req_47, s_n_llhttp__internal__n_start_req_46, s_n_llhttp__internal__n_start_req_54, s_n_llhttp__internal__n_start_req_56, s_n_llhttp__internal__n_start_req_57, s_n_llhttp__internal__n_start_req_55, s_n_llhttp__internal__n_start_req_53, s_n_llhttp__internal__n_start_req_58, s_n_llhttp__internal__n_start_req_59, s_n_llhttp__internal__n_start_req_52, s_n_llhttp__internal__n_start_req_61, s_n_llhttp__internal__n_start_req_62, s_n_llhttp__internal__n_start_req_60, s_n_llhttp__internal__n_start_req_65, s_n_llhttp__internal__n_start_req_67, s_n_llhttp__internal__n_start_req_68, s_n_llhttp__internal__n_start_req_66, s_n_llhttp__internal__n_start_req_69, s_n_llhttp__internal__n_start_req_64, s_n_llhttp__internal__n_start_req_63, s_n_llhttp__internal__n_start_req, s_n_llhttp__internal__n_invoke_llhttp__on_status_complete, s_n_llhttp__internal__n_res_line_almost_done, s_n_llhttp__internal__n_res_status, s_n_llhttp__internal__n_span_start_llhttp__on_status, s_n_llhttp__internal__n_res_status_start, s_n_llhttp__internal__n_res_status_code_otherwise, s_n_llhttp__internal__n_res_status_code, s_n_llhttp__internal__n_res_http_end, s_n_llhttp__internal__n_res_http_minor, s_n_llhttp__internal__n_res_http_dot, s_n_llhttp__internal__n_res_http_major, s_n_llhttp__internal__n_start_res, s_n_llhttp__internal__n_req_or_res_method_2, s_n_llhttp__internal__n_req_or_res_method_3, s_n_llhttp__internal__n_req_or_res_method_1, s_n_llhttp__internal__n_req_or_res_method, s_n_llhttp__internal__n_start_req_or_res, s_n_llhttp__internal__n_invoke_load_type, s_n_llhttp__internal__n_start, }; typedef enum llparse_state_e llparse_state_t; int llhttp__on_url( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_header_field( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_header_value( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_body( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_status( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_finish( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 2; return 0; } int llhttp__on_message_begin( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_type( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->type; } int llhttp__internal__c_store_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->method = match; return 0; } int llhttp__internal__c_is_equal_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->method == 5; } int llhttp__internal__c_update_http_major( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->http_major = 0; return 0; } int llhttp__internal__c_update_http_minor( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->http_minor = 9; return 0; } int llhttp__on_url_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_test_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 128) == 128; } int llhttp__on_chunk_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_message_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_is_equal_upgrade( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->upgrade == 1; } int llhttp__after_message_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_finish_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 0; return 0; } int llhttp__internal__c_test_lenient_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 4) == 4; } int llhttp__internal__c_test_flags_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 544) == 544; } int llhttp__internal__c_test_lenient_flags_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 2) == 2; } int llhttp__before_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__on_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__after_headers_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->content_length = 0; return 0; } int llhttp__internal__c_mul_add_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->content_length > 0xffffffffffffffffULL / 16) { return 1; } state->content_length *= 16; /* Addition overflow */ if (match >= 0) { if (state->content_length > 0xffffffffffffffffULL - match) { return 1; } } else { if (state->content_length < 0ULL - match) { return 1; } } state->content_length += match; return 0; } int llhttp__on_chunk_header( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_is_equal_content_length( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->content_length == 0; } int llhttp__internal__c_or_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 128; return 0; } int llhttp__internal__c_update_finish_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->finish = 1; return 0; } int llhttp__internal__c_or_flags_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 64; return 0; } int llhttp__internal__c_update_upgrade( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->upgrade = 1; return 0; } int llhttp__internal__c_store_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->header_state = match; return 0; } int llhttp__internal__c_test_lenient_flags_2( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->lenient_flags & 1) == 1; } int llhttp__on_header_field_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_load_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->header_state; } int llhttp__internal__c_or_flags_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 1; return 0; } int llhttp__internal__c_update_header_state( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 1; return 0; } int llhttp__on_header_value_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_or_flags_4( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 2; return 0; } int llhttp__internal__c_or_flags_5( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 4; return 0; } int llhttp__internal__c_or_flags_6( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 8; return 0; } int llhttp__internal__c_update_header_state_2( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 6; return 0; } int llhttp__internal__c_update_header_state_4( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 0; return 0; } int llhttp__internal__c_update_header_state_5( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 5; return 0; } int llhttp__internal__c_update_header_state_6( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 7; return 0; } int llhttp__internal__c_test_flags_2( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return (state->flags & 32) == 32; } int llhttp__internal__c_mul_add_content_length_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->content_length > 0xffffffffffffffffULL / 10) { return 1; } state->content_length *= 10; /* Addition overflow */ if (match >= 0) { if (state->content_length > 0xffffffffffffffffULL - match) { return 1; } } else { if (state->content_length < 0ULL - match) { return 1; } } state->content_length += match; return 0; } int llhttp__internal__c_or_flags_15( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 32; return 0; } int llhttp__internal__c_or_flags_16( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 512; return 0; } int llhttp__internal__c_and_flags( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags &= -9; return 0; } int llhttp__internal__c_update_header_state_7( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->header_state = 8; return 0; } int llhttp__internal__c_or_flags_17( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->flags |= 16; return 0; } int llhttp__internal__c_load_method( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { return state->method; } int llhttp__internal__c_store_http_major( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->http_major = match; return 0; } int llhttp__internal__c_store_http_minor( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { state->http_minor = match; return 0; } int llhttp__internal__c_update_status_code( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->status_code = 0; return 0; } int llhttp__internal__c_mul_add_status_code( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp, int match) { /* Multiplication overflow */ if (state->status_code > 0xffff / 10) { return 1; } state->status_code *= 10; /* Addition overflow */ if (match >= 0) { if (state->status_code > 0xffff - match) { return 1; } } else { if (state->status_code < 0 - match) { return 1; } } state->status_code += match; /* Enforce maximum */ if (state->status_code > 999) { return 1; } return 0; } int llhttp__on_status_complete( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); int llhttp__internal__c_update_type( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->type = 1; return 0; } int llhttp__internal__c_update_type_1( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { state->type = 2; return 0; } int llhttp__internal_init(llhttp__internal_t* state) { memset(state, 0, sizeof(*state)); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start; return 0; } static llparse_state_t llhttp__internal__run( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { int match; switch ((llparse_state_t) (intptr_t) state->_current) { case s_n_llhttp__internal__n_closed: s_n_llhttp__internal__n_closed: { if (p == endp) { return s_n_llhttp__internal__n_closed; } p++; goto s_n_llhttp__internal__n_closed; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: { switch (llhttp__after_message_complete(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_update_finish_2; default: goto s_n_llhttp__internal__n_invoke_update_finish_1; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_pause_1: s_n_llhttp__internal__n_pause_1: { state->error = 0x16; state->reason = "Pause on CONNECT/Upgrade"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; return s_error; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_is_equal_upgrade: s_n_llhttp__internal__n_invoke_is_equal_upgrade: { switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; default: goto s_n_llhttp__internal__n_pause_1; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_is_equal_upgrade; case 21: goto s_n_llhttp__internal__n_pause_5; default: goto s_n_llhttp__internal__n_error_10; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_data_almost_done_skip: s_n_llhttp__internal__n_chunk_data_almost_done_skip: { if (p == endp) { return s_n_llhttp__internal__n_chunk_data_almost_done_skip; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_data_almost_done: s_n_llhttp__internal__n_chunk_data_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_chunk_data_almost_done; } p++; goto s_n_llhttp__internal__n_chunk_data_almost_done_skip; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_consume_content_length: s_n_llhttp__internal__n_consume_content_length: { size_t avail; uint64_t need; avail = endp - p; need = state->content_length; if (avail >= need) { p += need; state->content_length = 0; goto s_n_llhttp__internal__n_span_end_llhttp__on_body; } state->content_length -= avail; return s_n_llhttp__internal__n_consume_content_length; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_body: s_n_llhttp__internal__n_span_start_llhttp__on_body: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_consume_content_length; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_is_equal_content_length: s_n_llhttp__internal__n_invoke_is_equal_content_length: { switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_start_llhttp__on_body; default: goto s_n_llhttp__internal__n_invoke_or_flags; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_size_almost_done: s_n_llhttp__internal__n_chunk_size_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_almost_done; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_parameters: s_n_llhttp__internal__n_chunk_parameters: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_chunk_parameters; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_chunk_parameters; } case 2: { p++; goto s_n_llhttp__internal__n_chunk_size_almost_done; } default: { goto s_n_llhttp__internal__n_error_6; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_size_otherwise: s_n_llhttp__internal__n_chunk_size_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_otherwise; } switch (*p) { case 13: { p++; goto s_n_llhttp__internal__n_chunk_size_almost_done; } case ' ': { p++; goto s_n_llhttp__internal__n_chunk_parameters; } case ';': { p++; goto s_n_llhttp__internal__n_chunk_parameters; } default: { goto s_n_llhttp__internal__n_error_7; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_size: s_n_llhttp__internal__n_chunk_size: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'A': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'B': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'C': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'D': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'E': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'F': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'a': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'b': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'c': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'd': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'e': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'f': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } default: { goto s_n_llhttp__internal__n_chunk_size_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_chunk_size_digit: s_n_llhttp__internal__n_chunk_size_digit: { if (p == endp) { return s_n_llhttp__internal__n_chunk_size_digit; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'A': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'B': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'C': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'D': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'E': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'F': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'a': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'b': { p++; match = 11; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'c': { p++; match = 12; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'd': { p++; match = 13; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'e': { p++; match = 14; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } case 'f': { p++; match = 15; goto s_n_llhttp__internal__n_invoke_mul_add_content_length; } default: { goto s_n_llhttp__internal__n_error_9; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_update_content_length: s_n_llhttp__internal__n_invoke_update_content_length: { switch (llhttp__internal__c_update_content_length(state, p, endp)) { default: goto s_n_llhttp__internal__n_chunk_size_digit; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_consume_content_length_1: s_n_llhttp__internal__n_consume_content_length_1: { size_t avail; uint64_t need; avail = endp - p; need = state->content_length; if (avail >= need) { p += need; state->content_length = 0; goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1; } state->content_length -= avail; return s_n_llhttp__internal__n_consume_content_length_1; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_body_1: s_n_llhttp__internal__n_span_start_llhttp__on_body_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_consume_content_length_1; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_eof: s_n_llhttp__internal__n_eof: { if (p == endp) { return s_n_llhttp__internal__n_eof; } p++; goto s_n_llhttp__internal__n_eof; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_body_2: s_n_llhttp__internal__n_span_start_llhttp__on_body_2: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_body_2; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_body; goto s_n_llhttp__internal__n_eof; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: { switch (llhttp__after_headers_complete(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1; case 2: goto s_n_llhttp__internal__n_invoke_update_content_length; case 3: goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1; case 4: goto s_n_llhttp__internal__n_invoke_update_finish_3; case 5: goto s_n_llhttp__internal__n_error_11; default: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_headers_almost_done: s_n_llhttp__internal__n_headers_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_headers_almost_done; } p++; goto s_n_llhttp__internal__n_invoke_test_flags; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_colon_discard_ws: s_n_llhttp__internal__n_header_field_colon_discard_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_field_colon_discard_ws; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_header_field_colon_discard_ws; } default: { goto s_n_llhttp__internal__n_header_field_colon; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_error_14: s_n_llhttp__internal__n_error_14: { state->error = 0xa; state->reason = "Invalid header field char"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: { switch (llhttp__on_header_value_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_header_value: s_n_llhttp__internal__n_span_start_llhttp__on_header_value: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_value; goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_discard_lws: s_n_llhttp__internal__n_header_value_discard_lws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_lws; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } default: { goto s_n_llhttp__internal__n_invoke_load_header_state; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_discard_ws_almost_done: s_n_llhttp__internal__n_header_value_discard_ws_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_ws_almost_done; } p++; goto s_n_llhttp__internal__n_header_value_discard_lws; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_lws: s_n_llhttp__internal__n_header_value_lws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_lws; } switch (*p) { case 9: { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } case ' ': { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } default: { goto s_n_llhttp__internal__n_invoke_load_header_state_3; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_almost_done: s_n_llhttp__internal__n_header_value_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_header_value_almost_done; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_value_lws; } default: { goto s_n_llhttp__internal__n_error_16; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_lenient: s_n_llhttp__internal__n_header_value_lenient: { if (p == endp) { return s_n_llhttp__internal__n_header_value_lenient; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; } default: { p++; goto s_n_llhttp__internal__n_header_value_lenient; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_otherwise: s_n_llhttp__internal__n_header_value_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_header_value_otherwise; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; } default: { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_token: s_n_llhttp__internal__n_header_value_connection_token: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_token; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value_connection_token; } case 2: { p++; goto s_n_llhttp__internal__n_header_value_connection; } default: { goto s_n_llhttp__internal__n_header_value_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_ws: s_n_llhttp__internal__n_header_value_connection_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_ws; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_header_value_otherwise; } case 13: { goto s_n_llhttp__internal__n_header_value_otherwise; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_connection_ws; } case ',': { p++; goto s_n_llhttp__internal__n_invoke_load_header_state_4; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_4; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_1: s_n_llhttp__internal__n_header_value_connection_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_1; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_2; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_2: s_n_llhttp__internal__n_header_value_connection_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_2; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_5; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection_3: s_n_llhttp__internal__n_header_value_connection_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_3; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob5, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_header_state_6; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_connection_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_connection_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_connection: s_n_llhttp__internal__n_header_value_connection: { if (p == endp) { return s_n_llhttp__internal__n_header_value_connection; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_connection; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_connection; } case 'c': { p++; goto s_n_llhttp__internal__n_header_value_connection_1; } case 'k': { p++; goto s_n_llhttp__internal__n_header_value_connection_2; } case 'u': { p++; goto s_n_llhttp__internal__n_header_value_connection_3; } default: { goto s_n_llhttp__internal__n_header_value_connection_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_error_19: s_n_llhttp__internal__n_error_19: { state->error = 0xb; state->reason = "Content-Length overflow"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_error_20: s_n_llhttp__internal__n_error_20: { state->error = 0xb; state->reason = "Invalid character in Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_content_length_ws: s_n_llhttp__internal__n_header_value_content_length_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_content_length_ws; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_invoke_or_flags_15; } case 13: { goto s_n_llhttp__internal__n_invoke_or_flags_15; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_content_length_ws; } default: { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_content_length: s_n_llhttp__internal__n_header_value_content_length: { if (p == endp) { return s_n_llhttp__internal__n_header_value_content_length; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; } default: { goto s_n_llhttp__internal__n_header_value_content_length_ws; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_te_chunked_last: s_n_llhttp__internal__n_header_value_te_chunked_last: { if (p == endp) { return s_n_llhttp__internal__n_header_value_te_chunked_last; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_invoke_update_header_state_7; } case 13: { goto s_n_llhttp__internal__n_invoke_update_header_state_7; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_te_chunked_last; } default: { goto s_n_llhttp__internal__n_header_value_te_chunked; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_te_token_ows: s_n_llhttp__internal__n_header_value_te_token_ows: { if (p == endp) { return s_n_llhttp__internal__n_header_value_te_token_ows; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } default: { goto s_n_llhttp__internal__n_header_value_te_chunked; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value: s_n_llhttp__internal__n_header_value: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value; } #ifdef __SSE4_2__ if (endp - p >= 16) { __m128i ranges; __m128i input; int avail; int match_len; /* Load input */ input = _mm_loadu_si128((__m128i const*) p); ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 6, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_value; } goto s_n_llhttp__internal__n_header_value_otherwise; } #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value; } default: { goto s_n_llhttp__internal__n_header_value_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_te_token: s_n_llhttp__internal__n_header_value_te_token: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_header_value_te_token; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_value_te_token; } case 2: { p++; goto s_n_llhttp__internal__n_header_value_te_token_ows; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_8; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_te_chunked: s_n_llhttp__internal__n_header_value_te_chunked: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_value_te_chunked; } match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_header_value_te_chunked_last; } case kMatchPause: { return s_n_llhttp__internal__n_header_value_te_chunked; } case kMatchMismatch: { goto s_n_llhttp__internal__n_header_value_te_token; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_value; goto s_n_llhttp__internal__n_invoke_load_header_state_2; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_value_discard_ws: s_n_llhttp__internal__n_header_value_discard_ws: { if (p == endp) { return s_n_llhttp__internal__n_header_value_discard_ws; } switch (*p) { case 9: { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } case 10: { p++; goto s_n_llhttp__internal__n_header_value_discard_lws; } case 13: { p++; goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done; } case ' ': { p++; goto s_n_llhttp__internal__n_header_value_discard_ws; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: { switch (llhttp__on_header_field_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_discard_ws; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_general_otherwise: s_n_llhttp__internal__n_header_field_general_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_header_field_general_otherwise; } switch (*p) { case ':': { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2; } default: { goto s_n_llhttp__internal__n_error_21; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_general: s_n_llhttp__internal__n_header_field_general: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_header_field_general; } #ifdef __SSE4_2__ if (endp - p >= 16) { __m128i ranges; __m128i input; int avail; int match_len; /* Load input */ input = _mm_loadu_si128((__m128i const*) p); ranges = _mm_loadu_si128((__m128i const*) llparse_blob8); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 16, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_field_general; } ranges = _mm_loadu_si128((__m128i const*) llparse_blob9); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 2, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_header_field_general; } goto s_n_llhttp__internal__n_header_field_general_otherwise; } #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_header_field_general; } default: { goto s_n_llhttp__internal__n_header_field_general_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_colon: s_n_llhttp__internal__n_header_field_colon: { if (p == endp) { return s_n_llhttp__internal__n_header_field_colon; } switch (*p) { case ' ': { goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2; } case ':': { goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_9; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_3: s_n_llhttp__internal__n_header_field_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_3; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_4: s_n_llhttp__internal__n_header_field_4: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_4; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 10); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_4; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_2: s_n_llhttp__internal__n_header_field_2: { if (p == endp) { return s_n_llhttp__internal__n_header_field_2; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 'n': { p++; goto s_n_llhttp__internal__n_header_field_3; } case 't': { p++; goto s_n_llhttp__internal__n_header_field_4; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_1: s_n_llhttp__internal__n_header_field_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_1; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_header_field_2; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_5: s_n_llhttp__internal__n_header_field_5: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_5; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 15); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_5; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_6: s_n_llhttp__internal__n_header_field_6: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_6; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 16); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_6; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_7: s_n_llhttp__internal__n_header_field_7: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_header_field_7; } match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob13, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_header_state; } case kMatchPause: { return s_n_llhttp__internal__n_header_field_7; } case kMatchMismatch: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field: s_n_llhttp__internal__n_header_field: { if (p == endp) { return s_n_llhttp__internal__n_header_field; } switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { case 'c': { p++; goto s_n_llhttp__internal__n_header_field_1; } case 'p': { p++; goto s_n_llhttp__internal__n_header_field_5; } case 't': { p++; goto s_n_llhttp__internal__n_header_field_6; } case 'u': { p++; goto s_n_llhttp__internal__n_header_field_7; } default: { goto s_n_llhttp__internal__n_invoke_update_header_state_10; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_header_field: s_n_llhttp__internal__n_span_start_llhttp__on_header_field: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_header_field; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_header_field; goto s_n_llhttp__internal__n_header_field; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_header_field_start: s_n_llhttp__internal__n_header_field_start: { if (p == endp) { return s_n_llhttp__internal__n_header_field_start; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_headers_almost_done; } case 13: { p++; goto s_n_llhttp__internal__n_headers_almost_done; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_skip_to_http09: s_n_llhttp__internal__n_url_skip_to_http09: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_to_http09; } p++; goto s_n_llhttp__internal__n_invoke_update_http_major; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_skip_lf_to_http09: s_n_llhttp__internal__n_url_skip_lf_to_http09: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_url_skip_lf_to_http09; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_http_major; } case kMatchPause: { return s_n_llhttp__internal__n_url_skip_lf_to_http09; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_22; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_pri_upgrade: s_n_llhttp__internal__n_req_pri_upgrade: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_pri_upgrade; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 10); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_error_25; } case kMatchPause: { return s_n_llhttp__internal__n_req_pri_upgrade; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_26; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_complete_1: s_n_llhttp__internal__n_req_http_complete_1: { if (p == endp) { return s_n_llhttp__internal__n_req_http_complete_1; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_field_start; } default: { goto s_n_llhttp__internal__n_error_24; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_complete: s_n_llhttp__internal__n_req_http_complete: { if (p == endp) { return s_n_llhttp__internal__n_req_http_complete; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_header_field_start; } case 13: { p++; goto s_n_llhttp__internal__n_req_http_complete_1; } default: { goto s_n_llhttp__internal__n_error_24; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_minor: s_n_llhttp__internal__n_req_http_minor: { if (p == endp) { return s_n_llhttp__internal__n_req_http_minor; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_minor; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_minor; } default: { goto s_n_llhttp__internal__n_error_27; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_dot: s_n_llhttp__internal__n_req_http_dot: { if (p == endp) { return s_n_llhttp__internal__n_req_http_dot; } switch (*p) { case '.': { p++; goto s_n_llhttp__internal__n_req_http_minor; } default: { goto s_n_llhttp__internal__n_error_28; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_major: s_n_llhttp__internal__n_req_http_major: { if (p == endp) { return s_n_llhttp__internal__n_req_http_major; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_major; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_major; } default: { goto s_n_llhttp__internal__n_error_29; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_start_1: s_n_llhttp__internal__n_req_http_start_1: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_http_start_1; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_load_method; } case kMatchPause: { return s_n_llhttp__internal__n_req_http_start_1; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_32; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_start_2: s_n_llhttp__internal__n_req_http_start_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_http_start_2; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_load_method_2; } case kMatchPause: { return s_n_llhttp__internal__n_req_http_start_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_32; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_start_3: s_n_llhttp__internal__n_req_http_start_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_http_start_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_load_method_3; } case kMatchPause: { return s_n_llhttp__internal__n_req_http_start_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_32; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_http_start: s_n_llhttp__internal__n_req_http_start: { if (p == endp) { return s_n_llhttp__internal__n_req_http_start; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_http_start; } case 'H': { p++; goto s_n_llhttp__internal__n_req_http_start_1; } case 'I': { p++; goto s_n_llhttp__internal__n_req_http_start_2; } case 'R': { p++; goto s_n_llhttp__internal__n_req_http_start_3; } default: { goto s_n_llhttp__internal__n_error_32; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_skip_to_http: s_n_llhttp__internal__n_url_skip_to_http: { if (p == endp) { return s_n_llhttp__internal__n_url_skip_to_http; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_fragment: s_n_llhttp__internal__n_url_fragment: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_url_fragment; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_url_fragment; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8; } default: { goto s_n_llhttp__internal__n_error_33; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_end_stub_query_3: s_n_llhttp__internal__n_span_end_stub_query_3: { if (p == endp) { return s_n_llhttp__internal__n_span_end_stub_query_3; } p++; goto s_n_llhttp__internal__n_url_fragment; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_query: s_n_llhttp__internal__n_url_query: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_url_query; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_url_query; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10; } case 4: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11; } case 5: { goto s_n_llhttp__internal__n_span_end_stub_query_3; } default: { goto s_n_llhttp__internal__n_error_34; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_query_or_fragment: s_n_llhttp__internal__n_url_query_or_fragment: { if (p == endp) { return s_n_llhttp__internal__n_url_query_or_fragment; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4; } case ' ': { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5; } case '#': { p++; goto s_n_llhttp__internal__n_url_fragment; } case '?': { p++; goto s_n_llhttp__internal__n_url_query; } default: { goto s_n_llhttp__internal__n_error_35; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_path: s_n_llhttp__internal__n_url_path: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; if (p == endp) { return s_n_llhttp__internal__n_url_path; } #ifdef __SSE4_2__ if (endp - p >= 16) { __m128i ranges; __m128i input; int avail; int match_len; /* Load input */ input = _mm_loadu_si128((__m128i const*) p); ranges = _mm_loadu_si128((__m128i const*) llparse_blob0); /* Find first character that does not match `ranges` */ match_len = _mm_cmpestri(ranges, 12, input, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY); if (match_len != 0) { p += match_len; goto s_n_llhttp__internal__n_url_path; } goto s_n_llhttp__internal__n_url_query_or_fragment; } #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_url_path; } default: { goto s_n_llhttp__internal__n_url_query_or_fragment; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_stub_path_2: s_n_llhttp__internal__n_span_start_stub_path_2: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path_2; } p++; goto s_n_llhttp__internal__n_url_path; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_stub_path: s_n_llhttp__internal__n_span_start_stub_path: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path; } p++; goto s_n_llhttp__internal__n_url_path; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_stub_path_1: s_n_llhttp__internal__n_span_start_stub_path_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_stub_path_1; } p++; goto s_n_llhttp__internal__n_url_path; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_server_with_at: s_n_llhttp__internal__n_url_server_with_at: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 6, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_server_with_at; } switch (lookup_table[(uint8_t) *p]) { case 1: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14; } case 4: { p++; goto s_n_llhttp__internal__n_url_server; } case 5: { goto s_n_llhttp__internal__n_span_start_stub_path_1; } case 6: { p++; goto s_n_llhttp__internal__n_url_query; } case 7: { p++; goto s_n_llhttp__internal__n_error_36; } default: { goto s_n_llhttp__internal__n_error_37; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_server: s_n_llhttp__internal__n_url_server: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 6, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_server; } switch (lookup_table[(uint8_t) *p]) { case 1: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url; } case 2: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1; } case 3: { goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2; } case 4: { p++; goto s_n_llhttp__internal__n_url_server; } case 5: { goto s_n_llhttp__internal__n_span_start_stub_path; } case 6: { p++; goto s_n_llhttp__internal__n_url_query; } case 7: { p++; goto s_n_llhttp__internal__n_url_server_with_at; } default: { goto s_n_llhttp__internal__n_error_38; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_schema_delim_1: s_n_llhttp__internal__n_url_schema_delim_1: { if (p == endp) { return s_n_llhttp__internal__n_url_schema_delim_1; } switch (*p) { case '/': { p++; goto s_n_llhttp__internal__n_url_server; } default: { goto s_n_llhttp__internal__n_error_40; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_schema_delim: s_n_llhttp__internal__n_url_schema_delim: { if (p == endp) { return s_n_llhttp__internal__n_url_schema_delim; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_error_39; } case 13: { p++; goto s_n_llhttp__internal__n_error_39; } case ' ': { p++; goto s_n_llhttp__internal__n_error_39; } case '/': { p++; goto s_n_llhttp__internal__n_url_schema_delim_1; } default: { goto s_n_llhttp__internal__n_error_40; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_end_stub_schema: s_n_llhttp__internal__n_span_end_stub_schema: { if (p == endp) { return s_n_llhttp__internal__n_span_end_stub_schema; } p++; goto s_n_llhttp__internal__n_url_schema_delim; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_schema: s_n_llhttp__internal__n_url_schema: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_schema; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_39; } case 2: { goto s_n_llhttp__internal__n_span_end_stub_schema; } case 3: { p++; goto s_n_llhttp__internal__n_url_schema; } default: { goto s_n_llhttp__internal__n_error_41; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_url_start: s_n_llhttp__internal__n_url_start: { static uint8_t lookup_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (p == endp) { return s_n_llhttp__internal__n_url_start; } switch (lookup_table[(uint8_t) *p]) { case 1: { p++; goto s_n_llhttp__internal__n_error_39; } case 2: { goto s_n_llhttp__internal__n_span_start_stub_path_2; } case 3: { goto s_n_llhttp__internal__n_url_schema; } default: { goto s_n_llhttp__internal__n_error_42; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_url_1: s_n_llhttp__internal__n_span_start_llhttp__on_url_1: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_url_1; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_url; goto s_n_llhttp__internal__n_url_start; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_url: s_n_llhttp__internal__n_span_start_llhttp__on_url: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_url; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_url; goto s_n_llhttp__internal__n_url_server; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_spaces_before_url: s_n_llhttp__internal__n_req_spaces_before_url: { if (p == endp) { return s_n_llhttp__internal__n_req_spaces_before_url; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_spaces_before_url; } default: { goto s_n_llhttp__internal__n_invoke_is_equal_method; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_first_space_before_url: s_n_llhttp__internal__n_req_first_space_before_url: { if (p == endp) { return s_n_llhttp__internal__n_req_first_space_before_url; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_req_spaces_before_url; } default: { goto s_n_llhttp__internal__n_error_43; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_2: s_n_llhttp__internal__n_start_req_2: { if (p == endp) { return s_n_llhttp__internal__n_start_req_2; } switch (*p) { case 'L': { p++; match = 19; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_3: s_n_llhttp__internal__n_start_req_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 36; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_1: s_n_llhttp__internal__n_start_req_1: { if (p == endp) { return s_n_llhttp__internal__n_start_req_1; } switch (*p) { case 'C': { p++; goto s_n_llhttp__internal__n_start_req_2; } case 'N': { p++; goto s_n_llhttp__internal__n_start_req_3; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_4: s_n_llhttp__internal__n_start_req_4: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_4; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 16; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_4; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_6: s_n_llhttp__internal__n_start_req_6: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_6; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 22; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_6; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_8: s_n_llhttp__internal__n_start_req_8: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_8; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_8; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_9: s_n_llhttp__internal__n_start_req_9: { if (p == endp) { return s_n_llhttp__internal__n_start_req_9; } switch (*p) { case 'Y': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_7: s_n_llhttp__internal__n_start_req_7: { if (p == endp) { return s_n_llhttp__internal__n_start_req_7; } switch (*p) { case 'N': { p++; goto s_n_llhttp__internal__n_start_req_8; } case 'P': { p++; goto s_n_llhttp__internal__n_start_req_9; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_5: s_n_llhttp__internal__n_start_req_5: { if (p == endp) { return s_n_llhttp__internal__n_start_req_5; } switch (*p) { case 'H': { p++; goto s_n_llhttp__internal__n_start_req_6; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_7; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_12: s_n_llhttp__internal__n_start_req_12: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_12; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_12; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_13: s_n_llhttp__internal__n_start_req_13: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_13; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 35; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_13; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_11: s_n_llhttp__internal__n_start_req_11: { if (p == endp) { return s_n_llhttp__internal__n_start_req_11; } switch (*p) { case 'L': { p++; goto s_n_llhttp__internal__n_start_req_12; } case 'S': { p++; goto s_n_llhttp__internal__n_start_req_13; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_10: s_n_llhttp__internal__n_start_req_10: { if (p == endp) { return s_n_llhttp__internal__n_start_req_10; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_start_req_11; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_14: s_n_llhttp__internal__n_start_req_14: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_14; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 45; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_14; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_17: s_n_llhttp__internal__n_start_req_17: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_17; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 41; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_17; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_16: s_n_llhttp__internal__n_start_req_16: { if (p == endp) { return s_n_llhttp__internal__n_start_req_16; } switch (*p) { case '_': { p++; goto s_n_llhttp__internal__n_start_req_17; } default: { match = 1; goto s_n_llhttp__internal__n_invoke_store_method_1; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_15: s_n_llhttp__internal__n_start_req_15: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_15; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_start_req_16; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_15; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_18: s_n_llhttp__internal__n_start_req_18: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_18; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_18; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_20: s_n_llhttp__internal__n_start_req_20: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_20; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 31; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_20; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_21: s_n_llhttp__internal__n_start_req_21: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_21; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_21; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_19: s_n_llhttp__internal__n_start_req_19: { if (p == endp) { return s_n_llhttp__internal__n_start_req_19; } switch (*p) { case 'I': { p++; goto s_n_llhttp__internal__n_start_req_20; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_21; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_23: s_n_llhttp__internal__n_start_req_23: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_23; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 24; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_23; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_24: s_n_llhttp__internal__n_start_req_24: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_24; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 23; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_24; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_26: s_n_llhttp__internal__n_start_req_26: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_26; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 21; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_26; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_28: s_n_llhttp__internal__n_start_req_28: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_28; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 30; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_28; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_29: s_n_llhttp__internal__n_start_req_29: { if (p == endp) { return s_n_llhttp__internal__n_start_req_29; } switch (*p) { case 'L': { p++; match = 10; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_27: s_n_llhttp__internal__n_start_req_27: { if (p == endp) { return s_n_llhttp__internal__n_start_req_27; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_28; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_29; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_25: s_n_llhttp__internal__n_start_req_25: { if (p == endp) { return s_n_llhttp__internal__n_start_req_25; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_26; } case 'C': { p++; goto s_n_llhttp__internal__n_start_req_27; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_30: s_n_llhttp__internal__n_start_req_30: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_30; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 11; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_30; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_22: s_n_llhttp__internal__n_start_req_22: { if (p == endp) { return s_n_llhttp__internal__n_start_req_22; } switch (*p) { case '-': { p++; goto s_n_llhttp__internal__n_start_req_23; } case 'E': { p++; goto s_n_llhttp__internal__n_start_req_24; } case 'K': { p++; goto s_n_llhttp__internal__n_start_req_25; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_30; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_31: s_n_llhttp__internal__n_start_req_31: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_31; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 25; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_31; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_32: s_n_llhttp__internal__n_start_req_32: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_32; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_32; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_35: s_n_llhttp__internal__n_start_req_35: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_35; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 28; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_35; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_36: s_n_llhttp__internal__n_start_req_36: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_36; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 39; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_36; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_34: s_n_llhttp__internal__n_start_req_34: { if (p == endp) { return s_n_llhttp__internal__n_start_req_34; } switch (*p) { case 'T': { p++; goto s_n_llhttp__internal__n_start_req_35; } case 'U': { p++; goto s_n_llhttp__internal__n_start_req_36; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_37: s_n_llhttp__internal__n_start_req_37: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_37; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 38; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_37; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_38: s_n_llhttp__internal__n_start_req_38: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_38; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_38; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_42: s_n_llhttp__internal__n_start_req_42: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_42; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 12; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_42; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_43: s_n_llhttp__internal__n_start_req_43: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_43; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 13; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_43; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_41: s_n_llhttp__internal__n_start_req_41: { if (p == endp) { return s_n_llhttp__internal__n_start_req_41; } switch (*p) { case 'F': { p++; goto s_n_llhttp__internal__n_start_req_42; } case 'P': { p++; goto s_n_llhttp__internal__n_start_req_43; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_40: s_n_llhttp__internal__n_start_req_40: { if (p == endp) { return s_n_llhttp__internal__n_start_req_40; } switch (*p) { case 'P': { p++; goto s_n_llhttp__internal__n_start_req_41; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_39: s_n_llhttp__internal__n_start_req_39: { if (p == endp) { return s_n_llhttp__internal__n_start_req_39; } switch (*p) { case 'I': { p++; match = 34; goto s_n_llhttp__internal__n_invoke_store_method_1; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_40; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_45: s_n_llhttp__internal__n_start_req_45: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_45; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 29; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_45; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_44: s_n_llhttp__internal__n_start_req_44: { if (p == endp) { return s_n_llhttp__internal__n_start_req_44; } switch (*p) { case 'R': { p++; goto s_n_llhttp__internal__n_start_req_45; } case 'T': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_33: s_n_llhttp__internal__n_start_req_33: { if (p == endp) { return s_n_llhttp__internal__n_start_req_33; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_34; } case 'L': { p++; goto s_n_llhttp__internal__n_start_req_37; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_38; } case 'R': { p++; goto s_n_llhttp__internal__n_start_req_39; } case 'U': { p++; goto s_n_llhttp__internal__n_start_req_44; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_48: s_n_llhttp__internal__n_start_req_48: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_48; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 17; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_48; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_49: s_n_llhttp__internal__n_start_req_49: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_49; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 44; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_49; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_50: s_n_llhttp__internal__n_start_req_50: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_50; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 43; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_50; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_51: s_n_llhttp__internal__n_start_req_51: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_51; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 20; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_51; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_47: s_n_llhttp__internal__n_start_req_47: { if (p == endp) { return s_n_llhttp__internal__n_start_req_47; } switch (*p) { case 'B': { p++; goto s_n_llhttp__internal__n_start_req_48; } case 'C': { p++; goto s_n_llhttp__internal__n_start_req_49; } case 'D': { p++; goto s_n_llhttp__internal__n_start_req_50; } case 'P': { p++; goto s_n_llhttp__internal__n_start_req_51; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_46: s_n_llhttp__internal__n_start_req_46: { if (p == endp) { return s_n_llhttp__internal__n_start_req_46; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_start_req_47; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_54: s_n_llhttp__internal__n_start_req_54: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_54; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 14; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_54; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_56: s_n_llhttp__internal__n_start_req_56: { if (p == endp) { return s_n_llhttp__internal__n_start_req_56; } switch (*p) { case 'P': { p++; match = 37; goto s_n_llhttp__internal__n_invoke_store_method_1; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_57: s_n_llhttp__internal__n_start_req_57: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_57; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 42; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_57; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_55: s_n_llhttp__internal__n_start_req_55: { if (p == endp) { return s_n_llhttp__internal__n_start_req_55; } switch (*p) { case 'U': { p++; goto s_n_llhttp__internal__n_start_req_56; } case '_': { p++; goto s_n_llhttp__internal__n_start_req_57; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_53: s_n_llhttp__internal__n_start_req_53: { if (p == endp) { return s_n_llhttp__internal__n_start_req_53; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_54; } case 'T': { p++; goto s_n_llhttp__internal__n_start_req_55; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_58: s_n_llhttp__internal__n_start_req_58: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_58; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 33; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_58; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_59: s_n_llhttp__internal__n_start_req_59: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_59; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 26; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_59; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_52: s_n_llhttp__internal__n_start_req_52: { if (p == endp) { return s_n_llhttp__internal__n_start_req_52; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_start_req_53; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_58; } case 'U': { p++; goto s_n_llhttp__internal__n_start_req_59; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_61: s_n_llhttp__internal__n_start_req_61: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_61; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 40; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_61; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_62: s_n_llhttp__internal__n_start_req_62: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_62; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_62; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_60: s_n_llhttp__internal__n_start_req_60: { if (p == endp) { return s_n_llhttp__internal__n_start_req_60; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_start_req_61; } case 'R': { p++; goto s_n_llhttp__internal__n_start_req_62; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_65: s_n_llhttp__internal__n_start_req_65: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_65; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 18; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_65; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_67: s_n_llhttp__internal__n_start_req_67: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_67; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 32; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_67; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_68: s_n_llhttp__internal__n_start_req_68: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_68; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 15; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_68; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_66: s_n_llhttp__internal__n_start_req_66: { if (p == endp) { return s_n_llhttp__internal__n_start_req_66; } switch (*p) { case 'I': { p++; goto s_n_llhttp__internal__n_start_req_67; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_68; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_69: s_n_llhttp__internal__n_start_req_69: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_req_69; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 8); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 27; goto s_n_llhttp__internal__n_invoke_store_method_1; } case kMatchPause: { return s_n_llhttp__internal__n_start_req_69; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_64: s_n_llhttp__internal__n_start_req_64: { if (p == endp) { return s_n_llhttp__internal__n_start_req_64; } switch (*p) { case 'B': { p++; goto s_n_llhttp__internal__n_start_req_65; } case 'L': { p++; goto s_n_llhttp__internal__n_start_req_66; } case 'S': { p++; goto s_n_llhttp__internal__n_start_req_69; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_63: s_n_llhttp__internal__n_start_req_63: { if (p == endp) { return s_n_llhttp__internal__n_start_req_63; } switch (*p) { case 'N': { p++; goto s_n_llhttp__internal__n_start_req_64; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req: s_n_llhttp__internal__n_start_req: { if (p == endp) { return s_n_llhttp__internal__n_start_req; } switch (*p) { case 'A': { p++; goto s_n_llhttp__internal__n_start_req_1; } case 'B': { p++; goto s_n_llhttp__internal__n_start_req_4; } case 'C': { p++; goto s_n_llhttp__internal__n_start_req_5; } case 'D': { p++; goto s_n_llhttp__internal__n_start_req_10; } case 'F': { p++; goto s_n_llhttp__internal__n_start_req_14; } case 'G': { p++; goto s_n_llhttp__internal__n_start_req_15; } case 'H': { p++; goto s_n_llhttp__internal__n_start_req_18; } case 'L': { p++; goto s_n_llhttp__internal__n_start_req_19; } case 'M': { p++; goto s_n_llhttp__internal__n_start_req_22; } case 'N': { p++; goto s_n_llhttp__internal__n_start_req_31; } case 'O': { p++; goto s_n_llhttp__internal__n_start_req_32; } case 'P': { p++; goto s_n_llhttp__internal__n_start_req_33; } case 'R': { p++; goto s_n_llhttp__internal__n_start_req_46; } case 'S': { p++; goto s_n_llhttp__internal__n_start_req_52; } case 'T': { p++; goto s_n_llhttp__internal__n_start_req_60; } case 'U': { p++; goto s_n_llhttp__internal__n_start_req_63; } default: { goto s_n_llhttp__internal__n_error_51; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: { switch (llhttp__on_status_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_line_almost_done: s_n_llhttp__internal__n_res_line_almost_done: { if (p == endp) { return s_n_llhttp__internal__n_res_line_almost_done; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_status: s_n_llhttp__internal__n_res_status: { if (p == endp) { return s_n_llhttp__internal__n_res_status; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_span_end_llhttp__on_status; } case 13: { goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1; } default: { p++; goto s_n_llhttp__internal__n_res_status; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_span_start_llhttp__on_status: s_n_llhttp__internal__n_span_start_llhttp__on_status: { if (p == endp) { return s_n_llhttp__internal__n_span_start_llhttp__on_status; } state->_span_pos0 = (void*) p; state->_span_cb0 = llhttp__on_status; goto s_n_llhttp__internal__n_res_status; /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_status_start: s_n_llhttp__internal__n_res_status_start: { if (p == endp) { return s_n_llhttp__internal__n_res_status_start; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; } case 13: { p++; goto s_n_llhttp__internal__n_res_line_almost_done; } default: { goto s_n_llhttp__internal__n_span_start_llhttp__on_status; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_status_code_otherwise: s_n_llhttp__internal__n_res_status_code_otherwise: { if (p == endp) { return s_n_llhttp__internal__n_res_status_code_otherwise; } switch (*p) { case 10: { goto s_n_llhttp__internal__n_res_status_start; } case 13: { goto s_n_llhttp__internal__n_res_status_start; } case ' ': { p++; goto s_n_llhttp__internal__n_res_status_start; } default: { goto s_n_llhttp__internal__n_error_45; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_status_code: s_n_llhttp__internal__n_res_status_code: { if (p == endp) { return s_n_llhttp__internal__n_res_status_code; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_mul_add_status_code; } default: { goto s_n_llhttp__internal__n_res_status_code_otherwise; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_http_end: s_n_llhttp__internal__n_res_http_end: { if (p == endp) { return s_n_llhttp__internal__n_res_http_end; } switch (*p) { case ' ': { p++; goto s_n_llhttp__internal__n_invoke_update_status_code; } default: { goto s_n_llhttp__internal__n_error_46; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_http_minor: s_n_llhttp__internal__n_res_http_minor: { if (p == endp) { return s_n_llhttp__internal__n_res_http_minor; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_minor_1; } default: { goto s_n_llhttp__internal__n_error_47; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_http_dot: s_n_llhttp__internal__n_res_http_dot: { if (p == endp) { return s_n_llhttp__internal__n_res_http_dot; } switch (*p) { case '.': { p++; goto s_n_llhttp__internal__n_res_http_minor; } default: { goto s_n_llhttp__internal__n_error_48; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_res_http_major: s_n_llhttp__internal__n_res_http_major: { if (p == endp) { return s_n_llhttp__internal__n_res_http_major; } switch (*p) { case '0': { p++; match = 0; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '1': { p++; match = 1; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '2': { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '3': { p++; match = 3; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '4': { p++; match = 4; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '5': { p++; match = 5; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '6': { p++; match = 6; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '7': { p++; match = 7; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '8': { p++; match = 8; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } case '9': { p++; match = 9; goto s_n_llhttp__internal__n_invoke_store_http_major_1; } default: { goto s_n_llhttp__internal__n_error_49; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_res: s_n_llhttp__internal__n_start_res: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_start_res; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_res_http_major; } case kMatchPause: { return s_n_llhttp__internal__n_start_res; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_52; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_or_res_method_2: s_n_llhttp__internal__n_req_or_res_method_2: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_2; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; match = 2; goto s_n_llhttp__internal__n_invoke_store_method; } case kMatchPause: { return s_n_llhttp__internal__n_req_or_res_method_2; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_50; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_or_res_method_3: s_n_llhttp__internal__n_req_or_res_method_3: { llparse_match_t match_seq; if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_3; } match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob61, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { p++; goto s_n_llhttp__internal__n_invoke_update_type_1; } case kMatchPause: { return s_n_llhttp__internal__n_req_or_res_method_3; } case kMatchMismatch: { goto s_n_llhttp__internal__n_error_50; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_or_res_method_1: s_n_llhttp__internal__n_req_or_res_method_1: { if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_1; } switch (*p) { case 'E': { p++; goto s_n_llhttp__internal__n_req_or_res_method_2; } case 'T': { p++; goto s_n_llhttp__internal__n_req_or_res_method_3; } default: { goto s_n_llhttp__internal__n_error_50; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_req_or_res_method: s_n_llhttp__internal__n_req_or_res_method: { if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method; } switch (*p) { case 'H': { p++; goto s_n_llhttp__internal__n_req_or_res_method_1; } default: { goto s_n_llhttp__internal__n_error_50; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start_req_or_res: s_n_llhttp__internal__n_start_req_or_res: { if (p == endp) { return s_n_llhttp__internal__n_start_req_or_res; } switch (*p) { case 'H': { goto s_n_llhttp__internal__n_req_or_res_method; } default: { goto s_n_llhttp__internal__n_invoke_update_type_2; } } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_invoke_load_type: s_n_llhttp__internal__n_invoke_load_type: { switch (llhttp__internal__c_load_type(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_start_req; case 2: goto s_n_llhttp__internal__n_start_res; default: goto s_n_llhttp__internal__n_start_req_or_res; } /* UNREACHABLE */; abort(); } case s_n_llhttp__internal__n_start: s_n_llhttp__internal__n_start: { if (p == endp) { return s_n_llhttp__internal__n_start; } switch (*p) { case 10: { p++; goto s_n_llhttp__internal__n_start; } case 13: { p++; goto s_n_llhttp__internal__n_start; } default: { goto s_n_llhttp__internal__n_invoke_update_finish; } } /* UNREACHABLE */; abort(); } default: /* UNREACHABLE */ abort(); } s_n_llhttp__internal__n_error_39: { state->error = 0x7; state->reason = "Invalid characters in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_finish_2: { switch (llhttp__internal__c_update_finish_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_start; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_lenient_flags: { switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_update_finish_2; default: goto s_n_llhttp__internal__n_closed; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_finish_1: { switch (llhttp__internal__c_update_finish_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_test_lenient_flags; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_5: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_10: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_7: { state->error = 0x15; state->reason = "on_chunk_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_13: { state->error = 0x14; state->reason = "`on_chunk_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: { switch (llhttp__on_chunk_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; case 21: goto s_n_llhttp__internal__n_pause_7; default: goto s_n_llhttp__internal__n_error_13; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_12: { state->error = 0x4; state->reason = "Content-Length can't be present with Transfer-Encoding"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_2: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_3: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_pause_1; case 21: goto s_n_llhttp__internal__n_pause_2; default: goto s_n_llhttp__internal__n_error_3; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_8: { state->error = 0xc; state->reason = "Chunk size overflow"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_3: { state->error = 0x15; state->reason = "on_chunk_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_5: { state->error = 0x14; state->reason = "`on_chunk_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: { switch (llhttp__on_chunk_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_update_content_length; case 21: goto s_n_llhttp__internal__n_pause_3; default: goto s_n_llhttp__internal__n_error_5; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_body: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_body(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done; return s_error; } goto s_n_llhttp__internal__n_chunk_data_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags: { switch (llhttp__internal__c_or_flags(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_4: { state->error = 0x15; state->reason = "on_chunk_header pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_4: { state->error = 0x13; state->reason = "`on_chunk_header` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: { switch (llhttp__on_chunk_header(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_is_equal_content_length; case 21: goto s_n_llhttp__internal__n_pause_4; default: goto s_n_llhttp__internal__n_error_4; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_6: { state->error = 0x2; state->reason = "Invalid character in chunk parameters"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_7: { state->error = 0xc; state->reason = "Invalid character in chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_mul_add_content_length: { switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_error_8; default: goto s_n_llhttp__internal__n_chunk_size; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_9: { state->error = 0xc; state->reason = "Invalid character in chunk size"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_body_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_body(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_finish_3: { switch (llhttp__internal__c_update_finish_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_11: { state->error = 0xf; state->reason = "Request has invalid `Transfer-Encoding`"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause: { state->error = 0x15; state->reason = "on_message_complete pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_2: { state->error = 0x12; state->reason = "`on_message_complete` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: { switch (llhttp__on_message_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; case 21: goto s_n_llhttp__internal__n_pause; default: goto s_n_llhttp__internal__n_error_2; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_1: { switch (llhttp__internal__c_or_flags_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_2: { switch (llhttp__internal__c_or_flags_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_upgrade: { switch (llhttp__internal__c_update_upgrade(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_or_flags_2; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_6: { state->error = 0x15; state->reason = "Paused by on_headers_complete"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_1: { state->error = 0x11; state->reason = "User callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: { switch (llhttp__on_headers_complete(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; case 1: goto s_n_llhttp__internal__n_invoke_or_flags_1; case 2: goto s_n_llhttp__internal__n_invoke_update_upgrade; case 21: goto s_n_llhttp__internal__n_pause_6; default: goto s_n_llhttp__internal__n_error_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: { switch (llhttp__before_headers_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_lenient_flags_1: { switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_error_12; default: goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_flags_1: { switch (llhttp__internal__c_test_flags_1(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1; default: goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_flags: { switch (llhttp__internal__c_test_flags(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1; default: goto s_n_llhttp__internal__n_invoke_test_flags_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_field: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_14; return s_error; } p++; goto s_n_llhttp__internal__n_error_14; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_lenient_flags_2: { switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_field_colon_discard_ws; default: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_15: { state->error = 0xb; state->reason = "Empty Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; return s_error; } goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_3: { switch (llhttp__internal__c_or_flags_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_4: { switch (llhttp__internal__c_or_flags_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_5: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_6: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state_1: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_3; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_4; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_5; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_6; default: goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 2: goto s_n_llhttp__internal__n_error_15; default: goto s_n_llhttp__internal__n_invoke_load_header_state_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_1: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_7: { switch (llhttp__internal__c_or_flags_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_8: { switch (llhttp__internal__c_or_flags_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_9: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_10: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state_3: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_7; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_8; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_9; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_10; default: goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_16: { state->error = 0x3; state->reason = "Missing expected LF after header value"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } goto s_n_llhttp__internal__n_header_value_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_header_value_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_header_value_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_17: { state->error = 0xa; state->reason = "Invalid header value char"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_lenient_flags_3: { switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_lenient; default: goto s_n_llhttp__internal__n_error_17; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_3: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_11: { switch (llhttp__internal__c_or_flags_3(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_3; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_12: { switch (llhttp__internal__c_or_flags_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_3; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_13: { switch (llhttp__internal__c_or_flags_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_3; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_14: { switch (llhttp__internal__c_or_flags_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state_4: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 5: goto s_n_llhttp__internal__n_invoke_or_flags_11; case 6: goto s_n_llhttp__internal__n_invoke_or_flags_12; case 7: goto s_n_llhttp__internal__n_invoke_or_flags_13; case 8: goto s_n_llhttp__internal__n_invoke_or_flags_14; default: goto s_n_llhttp__internal__n_header_value_connection; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_4: { switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_token; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_2: { switch (llhttp__internal__c_update_header_state_2(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_5: { switch (llhttp__internal__c_update_header_state_5(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_6: { switch (llhttp__internal__c_update_header_state_6(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_connection_ws; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_19; return s_error; } goto s_n_llhttp__internal__n_error_19; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; default: goto s_n_llhttp__internal__n_header_value_content_length; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_15: { switch (llhttp__internal__c_or_flags_15(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_otherwise; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_value(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_20; return s_error; } goto s_n_llhttp__internal__n_error_20; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_18: { state->error = 0x4; state->reason = "Duplicate Content-Length"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_test_flags_2: { switch (llhttp__internal__c_test_flags_2(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_header_value_content_length; default: goto s_n_llhttp__internal__n_error_18; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_7: { switch (llhttp__internal__c_update_header_state_7(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_otherwise; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_8: { switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_and_flags: { switch (llhttp__internal__c_and_flags(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_value_te_chunked; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_16: { switch (llhttp__internal__c_or_flags_16(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_and_flags; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_or_flags_17: { switch (llhttp__internal__c_or_flags_17(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_header_state_8; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_header_state_2: { switch (llhttp__internal__c_load_header_state(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_header_value_connection; case 2: goto s_n_llhttp__internal__n_invoke_test_flags_2; case 3: goto s_n_llhttp__internal__n_invoke_or_flags_16; case 4: goto s_n_llhttp__internal__n_invoke_or_flags_17; default: goto s_n_llhttp__internal__n_header_value; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_header_field(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_21: { state->error = 0xa; state->reason = "Invalid header token"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_9: { switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_general; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_header_state: { switch (llhttp__internal__c_store_header_state(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_header_field_colon; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_header_state_10: { switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_general; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: { switch (llhttp__on_url_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_header_field_start; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_http_minor: { switch (llhttp__internal__c_update_http_minor(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_http_major: { switch (llhttp__internal__c_update_http_major(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_update_http_minor; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_3: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_22: { state->error = 0x7; state->reason = "Expected CRLF"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_4: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_25: { state->error = 0x17; state->reason = "Pause on PRI/Upgrade"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_26: { state->error = 0x9; state->reason = "Expected HTTP/2 Connection Preface"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_24: { state->error = 0x9; state->reason = "Expected CRLF after version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_method_1: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 34: goto s_n_llhttp__internal__n_req_pri_upgrade; default: goto s_n_llhttp__internal__n_req_http_complete; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_http_minor: { switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_invoke_load_method_1; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_27: { state->error = 0x9; state->reason = "Invalid minor version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_28: { state->error = 0x9; state->reason = "Expected dot"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_http_major: { switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_req_http_dot; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_29: { state->error = 0x9; state->reason = "Invalid major version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_23: { state->error = 0x8; state->reason = "Invalid method for HTTP/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_method: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_req_http_major; case 1: goto s_n_llhttp__internal__n_req_http_major; case 2: goto s_n_llhttp__internal__n_req_http_major; case 3: goto s_n_llhttp__internal__n_req_http_major; case 4: goto s_n_llhttp__internal__n_req_http_major; case 5: goto s_n_llhttp__internal__n_req_http_major; case 6: goto s_n_llhttp__internal__n_req_http_major; case 7: goto s_n_llhttp__internal__n_req_http_major; case 8: goto s_n_llhttp__internal__n_req_http_major; case 9: goto s_n_llhttp__internal__n_req_http_major; case 10: goto s_n_llhttp__internal__n_req_http_major; case 11: goto s_n_llhttp__internal__n_req_http_major; case 12: goto s_n_llhttp__internal__n_req_http_major; case 13: goto s_n_llhttp__internal__n_req_http_major; case 14: goto s_n_llhttp__internal__n_req_http_major; case 15: goto s_n_llhttp__internal__n_req_http_major; case 16: goto s_n_llhttp__internal__n_req_http_major; case 17: goto s_n_llhttp__internal__n_req_http_major; case 18: goto s_n_llhttp__internal__n_req_http_major; case 19: goto s_n_llhttp__internal__n_req_http_major; case 20: goto s_n_llhttp__internal__n_req_http_major; case 21: goto s_n_llhttp__internal__n_req_http_major; case 22: goto s_n_llhttp__internal__n_req_http_major; case 23: goto s_n_llhttp__internal__n_req_http_major; case 24: goto s_n_llhttp__internal__n_req_http_major; case 25: goto s_n_llhttp__internal__n_req_http_major; case 26: goto s_n_llhttp__internal__n_req_http_major; case 27: goto s_n_llhttp__internal__n_req_http_major; case 28: goto s_n_llhttp__internal__n_req_http_major; case 29: goto s_n_llhttp__internal__n_req_http_major; case 30: goto s_n_llhttp__internal__n_req_http_major; case 31: goto s_n_llhttp__internal__n_req_http_major; case 32: goto s_n_llhttp__internal__n_req_http_major; case 33: goto s_n_llhttp__internal__n_req_http_major; case 34: goto s_n_llhttp__internal__n_req_http_major; default: goto s_n_llhttp__internal__n_error_23; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_32: { state->error = 0x8; state->reason = "Expected HTTP/"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_30: { state->error = 0x8; state->reason = "Expected SOURCE method for ICE/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_method_2: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 33: goto s_n_llhttp__internal__n_req_http_major; default: goto s_n_llhttp__internal__n_error_30; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_31: { state->error = 0x8; state->reason = "Invalid method for RTSP/x.x request"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_load_method_3: { switch (llhttp__internal__c_load_method(state, p, endp)) { case 1: goto s_n_llhttp__internal__n_req_http_major; case 3: goto s_n_llhttp__internal__n_req_http_major; case 6: goto s_n_llhttp__internal__n_req_http_major; case 35: goto s_n_llhttp__internal__n_req_http_major; case 36: goto s_n_llhttp__internal__n_req_http_major; case 37: goto s_n_llhttp__internal__n_req_http_major; case 38: goto s_n_llhttp__internal__n_req_http_major; case 39: goto s_n_llhttp__internal__n_req_http_major; case 40: goto s_n_llhttp__internal__n_req_http_major; case 41: goto s_n_llhttp__internal__n_req_http_major; case 42: goto s_n_llhttp__internal__n_req_http_major; case 43: goto s_n_llhttp__internal__n_req_http_major; case 44: goto s_n_llhttp__internal__n_req_http_major; case 45: goto s_n_llhttp__internal__n_req_http_major; default: goto s_n_llhttp__internal__n_error_31; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: { switch (llhttp__on_url_complete(state, p, endp)) { default: goto s_n_llhttp__internal__n_req_http_start; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_5: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_6: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_7: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_8: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_33: { state->error = 0x7; state->reason = "Invalid char in url fragment start"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_9: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_10: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_11: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_34: { state->error = 0x7; state->reason = "Invalid char in url query"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_35: { state->error = 0x7; state->reason = "Invalid char in url path"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_2: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_12: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_13: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; return s_error; } goto s_n_llhttp__internal__n_url_skip_lf_to_http09; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_url_14: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_url(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; return s_error; } goto s_n_llhttp__internal__n_url_skip_to_http; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_36: { state->error = 0x7; state->reason = "Double @ in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_37: { state->error = 0x7; state->reason = "Unexpected char in url server"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_38: { state->error = 0x7; state->reason = "Unexpected char in url server"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_40: { state->error = 0x7; state->reason = "Unexpected char in url schema"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_41: { state->error = 0x7; state->reason = "Unexpected char in url schema"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_42: { state->error = 0x7; state->reason = "Unexpected start char in url"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_is_equal_method: { switch (llhttp__internal__c_is_equal_method(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1; default: goto s_n_llhttp__internal__n_span_start_llhttp__on_url; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_43: { state->error = 0x6; state->reason = "Expected space after method"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_method_1: { switch (llhttp__internal__c_store_method(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_req_first_space_before_url; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_51: { state->error = 0x6; state->reason = "Invalid method encountered"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_44: { state->error = 0xd; state->reason = "Response overflow"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_mul_add_status_code: { switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { case 1: goto s_n_llhttp__internal__n_error_44; default: goto s_n_llhttp__internal__n_res_status_code; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_status: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_status(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; return s_error; } p++; goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_span_end_llhttp__on_status_1: { const unsigned char* start; int err; start = state->_span_pos0; state->_span_pos0 = NULL; err = llhttp__on_status(state, start, p); if (err != 0) { state->error = err; state->error_pos = (const char*) (p + 1); state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done; return s_error; } p++; goto s_n_llhttp__internal__n_res_line_almost_done; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_45: { state->error = 0xd; state->reason = "Invalid response status"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_status_code: { switch (llhttp__internal__c_update_status_code(state, p, endp)) { default: goto s_n_llhttp__internal__n_res_status_code; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_46: { state->error = 0x9; state->reason = "Expected space after version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_http_minor_1: { switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_res_http_end; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_47: { state->error = 0x9; state->reason = "Invalid minor version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_48: { state->error = 0x9; state->reason = "Expected dot"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_http_major_1: { switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_res_http_dot; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_49: { state->error = 0x9; state->reason = "Invalid major version"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_52: { state->error = 0x8; state->reason = "Expected HTTP/"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_type: { switch (llhttp__internal__c_update_type(state, p, endp)) { default: goto s_n_llhttp__internal__n_req_first_space_before_url; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_store_method: { switch (llhttp__internal__c_store_method(state, p, endp, match)) { default: goto s_n_llhttp__internal__n_invoke_update_type; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error_50: { state->error = 0x8; state->reason = "Invalid word encountered"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_type_1: { switch (llhttp__internal__c_update_type_1(state, p, endp)) { default: goto s_n_llhttp__internal__n_res_http_major; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_type_2: { switch (llhttp__internal__c_update_type(state, p, endp)) { default: goto s_n_llhttp__internal__n_start_req; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_pause_8: { state->error = 0x15; state->reason = "on_message_begin pause"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_error: { state->error = 0x10; state->reason = "`on_message_begin` callback error"; state->error_pos = (const char*) p; state->_current = (void*) (intptr_t) s_error; return s_error; /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: { switch (llhttp__on_message_begin(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_invoke_load_type; case 21: goto s_n_llhttp__internal__n_pause_8; default: goto s_n_llhttp__internal__n_error; } /* UNREACHABLE */; abort(); } s_n_llhttp__internal__n_invoke_update_finish: { switch (llhttp__internal__c_update_finish(state, p, endp)) { default: goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin; } /* UNREACHABLE */; abort(); } } int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) { llparse_state_t next; /* check lingering errors */ if (state->error != 0) { return state->error; } /* restart spans */ if (state->_span_pos0 != NULL) { state->_span_pos0 = (void*) p; } next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); if (next == s_error) { return state->error; } state->_current = (void*) (intptr_t) next; /* execute spans */ if (state->_span_pos0 != NULL) { int error; error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); if (error != 0) { state->error = error; state->error_pos = endp; return error; } } return 0; } #endif /* LLHTTP_STRICT_MODE */ ================================================ FILE: d2mapapi/simphttp/llhttp/llhttp.h ================================================ #ifndef INCLUDE_LLHTTP_H_ #define INCLUDE_LLHTTP_H_ #define LLHTTP_VERSION_MAJOR 6 #define LLHTTP_VERSION_MINOR 0 #define LLHTTP_VERSION_PATCH 6 #ifndef LLHTTP_STRICT_MODE # define LLHTTP_STRICT_MODE 0 #endif #ifndef INCLUDE_LLHTTP_ITSELF_H_ #define INCLUDE_LLHTTP_ITSELF_H_ #ifdef __cplusplus extern "C" { #endif #include typedef struct llhttp__internal_s llhttp__internal_t; struct llhttp__internal_s { int32_t _index; void* _span_pos0; void* _span_cb0; int32_t error; const char* reason; const char* error_pos; void* data; void* _current; uint64_t content_length; uint8_t type; uint8_t method; uint8_t http_major; uint8_t http_minor; uint8_t header_state; uint8_t lenient_flags; uint8_t upgrade; uint8_t finish; uint16_t flags; uint16_t status_code; void* settings; }; int llhttp__internal_init(llhttp__internal_t* s); int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* INCLUDE_LLHTTP_ITSELF_H_ */ #ifndef LLLLHTTP_C_HEADERS_ #define LLLLHTTP_C_HEADERS_ #ifdef __cplusplus extern "C" { #endif enum llhttp_errno { HPE_OK = 0, HPE_INTERNAL = 1, HPE_STRICT = 2, HPE_LF_EXPECTED = 3, HPE_UNEXPECTED_CONTENT_LENGTH = 4, HPE_CLOSED_CONNECTION = 5, HPE_INVALID_METHOD = 6, HPE_INVALID_URL = 7, HPE_INVALID_CONSTANT = 8, HPE_INVALID_VERSION = 9, HPE_INVALID_HEADER_TOKEN = 10, HPE_INVALID_CONTENT_LENGTH = 11, HPE_INVALID_CHUNK_SIZE = 12, HPE_INVALID_STATUS = 13, HPE_INVALID_EOF_STATE = 14, HPE_INVALID_TRANSFER_ENCODING = 15, HPE_CB_MESSAGE_BEGIN = 16, HPE_CB_HEADERS_COMPLETE = 17, HPE_CB_MESSAGE_COMPLETE = 18, HPE_CB_CHUNK_HEADER = 19, HPE_CB_CHUNK_COMPLETE = 20, HPE_PAUSED = 21, HPE_PAUSED_UPGRADE = 22, HPE_PAUSED_H2_UPGRADE = 23, HPE_USER = 24 }; typedef enum llhttp_errno llhttp_errno_t; enum llhttp_flags { F_CONNECTION_KEEP_ALIVE = 0x1, F_CONNECTION_CLOSE = 0x2, F_CONNECTION_UPGRADE = 0x4, F_CHUNKED = 0x8, F_UPGRADE = 0x10, F_CONTENT_LENGTH = 0x20, F_SKIPBODY = 0x40, F_TRAILING = 0x80, F_TRANSFER_ENCODING = 0x200 }; typedef enum llhttp_flags llhttp_flags_t; enum llhttp_lenient_flags { LENIENT_HEADERS = 0x1, LENIENT_CHUNKED_LENGTH = 0x2, LENIENT_KEEP_ALIVE = 0x4 }; typedef enum llhttp_lenient_flags llhttp_lenient_flags_t; enum llhttp_type { HTTP_BOTH = 0, HTTP_REQUEST = 1, HTTP_RESPONSE = 2 }; typedef enum llhttp_type llhttp_type_t; enum llhttp_finish { HTTP_FINISH_SAFE = 0, HTTP_FINISH_SAFE_WITH_CB = 1, HTTP_FINISH_UNSAFE = 2 }; typedef enum llhttp_finish llhttp_finish_t; enum llhttp_method { HTTP_DELETE = 0, HTTP_GET = 1, HTTP_HEAD = 2, HTTP_POST = 3, HTTP_PUT = 4, HTTP_CONNECT = 5, HTTP_OPTIONS = 6, HTTP_TRACE = 7, HTTP_COPY = 8, HTTP_LOCK = 9, HTTP_MKCOL = 10, HTTP_MOVE = 11, HTTP_PROPFIND = 12, HTTP_PROPPATCH = 13, HTTP_SEARCH = 14, HTTP_UNLOCK = 15, HTTP_BIND = 16, HTTP_REBIND = 17, HTTP_UNBIND = 18, HTTP_ACL = 19, HTTP_REPORT = 20, HTTP_MKACTIVITY = 21, HTTP_CHECKOUT = 22, HTTP_MERGE = 23, HTTP_MSEARCH = 24, HTTP_NOTIFY = 25, HTTP_SUBSCRIBE = 26, HTTP_UNSUBSCRIBE = 27, HTTP_PATCH = 28, HTTP_PURGE = 29, HTTP_MKCALENDAR = 30, HTTP_LINK = 31, HTTP_UNLINK = 32, HTTP_SOURCE = 33, HTTP_PRI = 34, HTTP_DESCRIBE = 35, HTTP_ANNOUNCE = 36, HTTP_SETUP = 37, HTTP_PLAY = 38, HTTP_PAUSE = 39, HTTP_TEARDOWN = 40, HTTP_GET_PARAMETER = 41, HTTP_SET_PARAMETER = 42, HTTP_REDIRECT = 43, HTTP_RECORD = 44, HTTP_FLUSH = 45 }; typedef enum llhttp_method llhttp_method_t; #define HTTP_ERRNO_MAP(XX) \ XX(0, OK, OK) \ XX(1, INTERNAL, INTERNAL) \ XX(2, STRICT, STRICT) \ XX(3, LF_EXPECTED, LF_EXPECTED) \ XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \ XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \ XX(6, INVALID_METHOD, INVALID_METHOD) \ XX(7, INVALID_URL, INVALID_URL) \ XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \ XX(9, INVALID_VERSION, INVALID_VERSION) \ XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \ XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \ XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \ XX(13, INVALID_STATUS, INVALID_STATUS) \ XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \ XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \ XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \ XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \ XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \ XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \ XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \ XX(21, PAUSED, PAUSED) \ XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \ XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \ XX(24, USER, USER) \ #define HTTP_METHOD_MAP(XX) \ XX(0, DELETE, DELETE) \ XX(1, GET, GET) \ XX(2, HEAD, HEAD) \ XX(3, POST, POST) \ XX(4, PUT, PUT) \ XX(5, CONNECT, CONNECT) \ XX(6, OPTIONS, OPTIONS) \ XX(7, TRACE, TRACE) \ XX(8, COPY, COPY) \ XX(9, LOCK, LOCK) \ XX(10, MKCOL, MKCOL) \ XX(11, MOVE, MOVE) \ XX(12, PROPFIND, PROPFIND) \ XX(13, PROPPATCH, PROPPATCH) \ XX(14, SEARCH, SEARCH) \ XX(15, UNLOCK, UNLOCK) \ XX(16, BIND, BIND) \ XX(17, REBIND, REBIND) \ XX(18, UNBIND, UNBIND) \ XX(19, ACL, ACL) \ XX(20, REPORT, REPORT) \ XX(21, MKACTIVITY, MKACTIVITY) \ XX(22, CHECKOUT, CHECKOUT) \ XX(23, MERGE, MERGE) \ XX(24, MSEARCH, M-SEARCH) \ XX(25, NOTIFY, NOTIFY) \ XX(26, SUBSCRIBE, SUBSCRIBE) \ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ XX(28, PATCH, PATCH) \ XX(29, PURGE, PURGE) \ XX(30, MKCALENDAR, MKCALENDAR) \ XX(31, LINK, LINK) \ XX(32, UNLINK, UNLINK) \ XX(33, SOURCE, SOURCE) \ #define RTSP_METHOD_MAP(XX) \ XX(1, GET, GET) \ XX(3, POST, POST) \ XX(6, OPTIONS, OPTIONS) \ XX(35, DESCRIBE, DESCRIBE) \ XX(36, ANNOUNCE, ANNOUNCE) \ XX(37, SETUP, SETUP) \ XX(38, PLAY, PLAY) \ XX(39, PAUSE, PAUSE) \ XX(40, TEARDOWN, TEARDOWN) \ XX(41, GET_PARAMETER, GET_PARAMETER) \ XX(42, SET_PARAMETER, SET_PARAMETER) \ XX(43, REDIRECT, REDIRECT) \ XX(44, RECORD, RECORD) \ XX(45, FLUSH, FLUSH) \ #define HTTP_ALL_METHOD_MAP(XX) \ XX(0, DELETE, DELETE) \ XX(1, GET, GET) \ XX(2, HEAD, HEAD) \ XX(3, POST, POST) \ XX(4, PUT, PUT) \ XX(5, CONNECT, CONNECT) \ XX(6, OPTIONS, OPTIONS) \ XX(7, TRACE, TRACE) \ XX(8, COPY, COPY) \ XX(9, LOCK, LOCK) \ XX(10, MKCOL, MKCOL) \ XX(11, MOVE, MOVE) \ XX(12, PROPFIND, PROPFIND) \ XX(13, PROPPATCH, PROPPATCH) \ XX(14, SEARCH, SEARCH) \ XX(15, UNLOCK, UNLOCK) \ XX(16, BIND, BIND) \ XX(17, REBIND, REBIND) \ XX(18, UNBIND, UNBIND) \ XX(19, ACL, ACL) \ XX(20, REPORT, REPORT) \ XX(21, MKACTIVITY, MKACTIVITY) \ XX(22, CHECKOUT, CHECKOUT) \ XX(23, MERGE, MERGE) \ XX(24, MSEARCH, M-SEARCH) \ XX(25, NOTIFY, NOTIFY) \ XX(26, SUBSCRIBE, SUBSCRIBE) \ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ XX(28, PATCH, PATCH) \ XX(29, PURGE, PURGE) \ XX(30, MKCALENDAR, MKCALENDAR) \ XX(31, LINK, LINK) \ XX(32, UNLINK, UNLINK) \ XX(33, SOURCE, SOURCE) \ XX(34, PRI, PRI) \ XX(35, DESCRIBE, DESCRIBE) \ XX(36, ANNOUNCE, ANNOUNCE) \ XX(37, SETUP, SETUP) \ XX(38, PLAY, PLAY) \ XX(39, PAUSE, PAUSE) \ XX(40, TEARDOWN, TEARDOWN) \ XX(41, GET_PARAMETER, GET_PARAMETER) \ XX(42, SET_PARAMETER, SET_PARAMETER) \ XX(43, REDIRECT, REDIRECT) \ XX(44, RECORD, RECORD) \ XX(45, FLUSH, FLUSH) \ #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LLLLHTTP_C_HEADERS_ */ #ifndef INCLUDE_LLHTTP_API_H_ #define INCLUDE_LLHTTP_API_H_ #ifdef __cplusplus extern "C" { #endif #include #if defined(__wasm__) #define LLHTTP_EXPORT __attribute__((visibility("default"))) #else #define LLHTTP_EXPORT #endif typedef llhttp__internal_t llhttp_t; typedef struct llhttp_settings_s llhttp_settings_t; typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length); typedef int (*llhttp_cb)(llhttp_t*); struct llhttp_settings_s { /* Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_message_begin; /* Possible return values 0, -1, HPE_USER */ llhttp_data_cb on_url; llhttp_data_cb on_status; llhttp_data_cb on_header_field; llhttp_data_cb on_header_value; /* Possible return values: * 0 - Proceed normally * 1 - Assume that request/response has no body, and proceed to parsing the * next message * 2 - Assume absence of body (as above) and make `llhttp_execute()` return * `HPE_PAUSED_UPGRADE` * -1 - Error * `HPE_PAUSED` */ llhttp_cb on_headers_complete; /* Possible return values 0, -1, HPE_USER */ llhttp_data_cb on_body; /* Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_message_complete; /* When on_chunk_header is called, the current chunk length is stored * in parser->content_length. * Possible return values 0, -1, `HPE_PAUSED` */ llhttp_cb on_chunk_header; llhttp_cb on_chunk_complete; /* Information-only callbacks, return value is ignored */ llhttp_cb on_url_complete; llhttp_cb on_status_complete; llhttp_cb on_header_field_complete; llhttp_cb on_header_value_complete; }; /* Initialize the parser with specific type and user settings. * * NOTE: lifetime of `settings` has to be at least the same as the lifetime of * the `parser` here. In practice, `settings` has to be either a static * variable or be allocated with `malloc`, `new`, etc. */ LLHTTP_EXPORT void llhttp_init(llhttp_t* parser, llhttp_type_t type, const llhttp_settings_t* settings); #if defined(__wasm__) LLHTTP_EXPORT llhttp_t* llhttp_alloc(llhttp_type_t type); LLHTTP_EXPORT void llhttp_free(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_type(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_http_major(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_http_minor(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_method(llhttp_t* parser); LLHTTP_EXPORT int llhttp_get_status_code(llhttp_t* parser); LLHTTP_EXPORT uint8_t llhttp_get_upgrade(llhttp_t* parser); #endif // defined(__wasm__) /* Reset an already initialized parser back to the start state, preserving the * existing parser type, callback settings, user data, and lenient flags. */ LLHTTP_EXPORT void llhttp_reset(llhttp_t* parser); /* Initialize the settings object */ LLHTTP_EXPORT void llhttp_settings_init(llhttp_settings_t* settings); /* Parse full or partial request/response, invoking user callbacks along the * way. * * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing * interrupts, and such errno is returned from `llhttp_execute()`. If * `HPE_PAUSED` was used as a errno, the execution can be resumed with * `llhttp_resume()` call. * * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE` * is returned after fully parsing the request/response. If the user wishes to * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`. * * NOTE: if this function ever returns a non-pause type error, it will continue * to return the same error upon each successive call up until `llhttp_init()` * is called. */ LLHTTP_EXPORT llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len); /* This method should be called when the other side has no further bytes to * send (e.g. shutdown of readable side of the TCP connection.) * * Requests without `Content-Length` and other messages might require treating * all incoming bytes as the part of the body, up to the last byte of the * connection. This method will invoke `on_message_complete()` callback if the * request was terminated safely. Otherwise a error code would be returned. */ LLHTTP_EXPORT llhttp_errno_t llhttp_finish(llhttp_t* parser); /* Returns `1` if the incoming message is parsed until the last byte, and has * to be completed by calling `llhttp_finish()` on EOF */ LLHTTP_EXPORT int llhttp_message_needs_eof(const llhttp_t* parser); /* Returns `1` if there might be any other messages following the last that was * successfully parsed. */ LLHTTP_EXPORT int llhttp_should_keep_alive(const llhttp_t* parser); /* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set * appropriate error reason. * * Important: do not call this from user callbacks! User callbacks must return * `HPE_PAUSED` if pausing is required. */ LLHTTP_EXPORT void llhttp_pause(llhttp_t* parser); /* Might be called to resume the execution after the pause in user's callback. * See `llhttp_execute()` above for details. * * Call this only if `llhttp_execute()` returns `HPE_PAUSED`. */ LLHTTP_EXPORT void llhttp_resume(llhttp_t* parser); /* Might be called to resume the execution after the pause in user's callback. * See `llhttp_execute()` above for details. * * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE` */ LLHTTP_EXPORT void llhttp_resume_after_upgrade(llhttp_t* parser); /* Returns the latest return error */ LLHTTP_EXPORT llhttp_errno_t llhttp_get_errno(const llhttp_t* parser); /* Returns the verbal explanation of the latest returned error. * * Note: User callback should set error reason when returning the error. See * `llhttp_set_error_reason()` for details. */ LLHTTP_EXPORT const char* llhttp_get_error_reason(const llhttp_t* parser); /* Assign verbal description to the returned error. Must be called in user * callbacks right before returning the errno. * * Note: `HPE_USER` error code might be useful in user callbacks. */ LLHTTP_EXPORT void llhttp_set_error_reason(llhttp_t* parser, const char* reason); /* Returns the pointer to the last parsed byte before the returned error. The * pointer is relative to the `data` argument of `llhttp_execute()`. * * Note: this method might be useful for counting the number of parsed bytes. */ LLHTTP_EXPORT const char* llhttp_get_error_pos(const llhttp_t* parser); /* Returns textual name of error code */ LLHTTP_EXPORT const char* llhttp_errno_name(llhttp_errno_t err); /* Returns textual name of HTTP method */ LLHTTP_EXPORT const char* llhttp_method_name(llhttp_method_t method); /* Enables/disables lenient header value parsing (disabled by default). * * Lenient parsing disables header value token checks, extending llhttp's * protocol support to highly non-compliant clients/server. No * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when * lenient parsing is "on". * * **(USE AT YOUR OWN RISK)** */ LLHTTP_EXPORT void llhttp_set_lenient_headers(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of conflicting `Transfer-Encoding` and * `Content-Length` headers (disabled by default). * * Normally `llhttp` would error when `Transfer-Encoding` is present in * conjunction with `Content-Length`. This error is important to prevent HTTP * request smuggling, but may be less desirable for small number of cases * involving legacy servers. * * **(USE AT YOUR OWN RISK)** */ LLHTTP_EXPORT void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled); /* Enables/disables lenient handling of `Connection: close` and HTTP/1.0 * requests responses. * * Normally `llhttp` would error on (in strict mode) or discard (in loose mode) * the HTTP request/response after the request/response with `Connection: close` * and `Content-Length`. This is important to prevent cache poisoning attacks, * but might interact badly with outdated and insecure clients. With this flag * the extra request/response will be parsed normally. * * **(USE AT YOUR OWN RISK)** */ void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* INCLUDE_LLHTTP_API_H_ */ #endif /* INCLUDE_LLHTTP_H_ */ ================================================ FILE: d2mapapi/simphttp/simphttp.cpp ================================================ #include "simphttp.h" #include namespace simphttp { const std::string CRLF = "\r\n"; Server::Server(Listener fn) : listener(std::move(fn)) { } Client::Client(std::string ustr, Listener fn) : listener(std::move(fn)) { auto u = uri::ParseHttpUrl(ustr); opts.host = u.host; if (u.port) { opts.port = u.port; } if (!u.path.empty()) { opts.url = uri::UriEncode(u.path); } connect(); } Client::Client(Client::Options o, Listener fn) : listener(std::move(fn)) { opts = std::move(o); listener = fn; connect(); } void free_context(uv_handle_t *handle) { auto *context = reinterpret_cast(handle->data); context->writes.clear(); free(context); } // // Events // template void attachEvents(Type *instance, llhttp_settings_t &settings) { // http parser callback types static std::function on_message_complete; static auto callback = instance->listener; // called once a connection has been made and the message is complete. on_message_complete = [&](llhttp_t *parser) -> int { return instance->complete(parser, callback); }; // called after the url has been parsed. settings.on_url = [](llhttp_t *parser, const char *at, size_t len) -> int { auto *context = static_cast(parser->data); if (at && context) { context->url = std::string(at, len); } return 0; }; // called when there are either fields or values in the request. settings.on_header_field = [](llhttp_t *parser, const char *at, size_t length) -> int { return 0; }; // called when header value is given settings.on_header_value = [](llhttp_t *parser, const char *at, size_t length) -> int { return 0; }; // called once all fields and values have been parsed. settings.on_headers_complete = [](llhttp_t *parser) -> int { auto *context = static_cast(parser->data); context->method = std::string(llhttp_method_name((llhttp_method_t)parser->method)); return 0; }; // called when there is a body for the request. settings.on_body = [](llhttp_t *parser, const char *at, size_t len) -> int { auto *context = static_cast(parser->data); if (at && context && (int)len > -1) { context->body << std::string_view(at, len); } return 0; }; // called after all other events. settings.on_message_complete = [](llhttp_t *parser) -> int { return on_message_complete(parser); }; } template void attachEvents(Client *instance, llhttp_settings_t &settings); template void attachEvents(Server *instance, llhttp_settings_t &settings); // // Response. // void Response::setHeader(const std::string &key, const std::string &val) { headersSet = true; if (writtenOrEnded) throw std::runtime_error("Can not set headers after write"); if (key == "Content-Length") { contentLengthSet = true; } headers.emplace(key, val); } void Response::setStatus(int code) { statusSet = true; if (writtenOrEnded) throw std::runtime_error("Can not set status after write"); statusCode = code; } void Response::setStatus(int code, const std::string &ad) { statusSet = true; if (writtenOrEnded) throw std::runtime_error("Can not set status after write"); statusCode = code; statusAdjective = ad; } void Response::writeOrEnd(const std::string &str, bool end) { if (ended) throw std::runtime_error("Can not write after end"); std::stringstream ss; if (!writtenOrEnded) { ss << "HTTP/1.1 " << statusCode << " " << statusAdjective << CRLF; for (auto &header: headers) { ss << header.first << ": " << header.second << CRLF; } ss << CRLF; writtenOrEnded = true; } bool isChunked = headers.count("Transfer-Encoding") && headers["Transfer-Encoding"] == "chunked"; if (isChunked) { ss << std::hex << str.size() << std::dec << CRLF << str << CRLF; } else { ss << str; } if (isChunked && end) { ss << "0" << CRLF << CRLF; } auto str2 = ss.str(); // response buffer uv_buf_t resbuf = { .len = (unsigned long)str2.size(), .base = (char *)str2.c_str(), }; auto *context = static_cast(this->parser.data); auto id = write_count++; uv_write_t write_req; context->writes.insert({id, write_req}); if (end) { ended = true; uv_write(&context->writes.at(id), (uv_stream_t *)&context->handle, &resbuf, 1, [](uv_write_t *req, int status) { if (!uv_is_closing((uv_handle_t *)req->handle)) { uv_close((uv_handle_t *)req->handle, free_context); } } ); } else { uv_write(&context->writes.at(id), (uv_stream_t *)&context->handle, &resbuf, 1, nullptr); } } void Response::write(const std::string &s) { this->writeOrEnd(s, false); } void Response::end(const std::string &s) { this->writeOrEnd(s, true); } void Response::end() { this->writeOrEnd("", true); } int Server::complete(llhttp_t *parser, const Listener &cb) { auto *context = reinterpret_cast(parser->data); Request req; Response res; req.url = uri::UriDecode(context->url); req.method = context->method; res.parser = *parser; cb(req, res); return 0; } int Server::listen(const char *ip, int port) { // // parser settings needs to be static. // // static llhttp_settings_t settings; attachEvents(this, settings); int status = 0; #ifdef _WIN32 SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); int cores = sysinfo.dwNumberOfProcessors; #else int cores = sysconf(_SC_NPROCESSORS_ONLN); #endif std::stringstream cores_string; cores_string << cores; #ifdef _WIN32 SetEnvironmentVariable("UV_THREADPOOL_SIZE", cores_string.str().c_str()); #else setenv("UV_THREADPOOL_SIZE", cores_string.str().c_str(), 1); #endif sockaddr_storage address = {}; static std::function on_connect; static std::function read; loop_ = uv_default_loop(); uv_tcp_init(loop_, &socket_); // // @TODO - Not sure exactly how to use this, // after the initial timeout, it just // seems to kill the server. // //uv_tcp_keepalive(&socket_,1,60); uv_tcp_nodelay(&socket_, 1); status = uv_ip6_addr(ip, port, (struct sockaddr_in6 *)&address); if (status != 0) { status = uv_ip4_addr(ip, port, (struct sockaddr_in *)&address); } ASSERT_STATUS(status, "Resolve Address"); status = uv_tcp_bind(&socket_, (const struct sockaddr *)&address, 0); ASSERT_STATUS(status, "Bind"); // called once a connection is made. on_connect = [&](uv_stream_t *handle, int status) { auto *context = new Context(); // init tcp handle uv_tcp_init(loop_, &context->handle); // init http parser llhttp_init(&context->parser, HTTP_REQUEST, &settings); // client reference for parser routines context->parser.data = context; // client reference for handle data on requests context->handle.data = context; // accept connection passing in refernce to the client handle uv_accept(handle, (uv_stream_t *)&context->handle); // called for every read read = [&](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) { ssize_t parsed; auto *context = static_cast(tcp->data); if (nread >= 0) { parsed = (ssize_t)llhttp_execute(&context->parser, buf->base, nread); // close handle if (parsed < nread) { uv_close((uv_handle_t *)&context->handle, free_context); } } else { if (nread != UV_EOF) { // @TODO - debug error } // close handle uv_close((uv_handle_t *)&context->handle, free_context); } // free request buffer data free(buf->base); }; // allocate memory and attempt to read. uv_read_start((uv_stream_t *)&context->handle, // allocator [](uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { *buf = uv_buf_init((char *)malloc(suggested_size), suggested_size); }, // reader [](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) { read(tcp, nread, buf); }); }; status = uv_listen((uv_stream_t *)&socket_, MAX_WRITE_HANDLES, // listener [](uv_stream_t *socket, int status) { on_connect(socket, status); }); ASSERT_STATUS(status, "Listen"); // init loop uv_run(loop_, UV_RUN_DEFAULT); return 0; } int Client::complete(llhttp_t *parser, const Listener &cb) { auto *context = reinterpret_cast(parser->data); Response res; res.body = context->body.str(); res.parser = *parser; cb(res); return 0; } void Client::on_connect(uv_connect_t *req, int status) { auto *context = reinterpret_cast(req->handle->data); if (status == -1) { // @TODO // Error Callback uv_close((uv_handle_t *)req->handle, free_context); return; } static std::function read; read = [&](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) { auto *context = static_cast(tcp->data); if (nread >= 0) { auto parsed = (ssize_t)llhttp_execute( &context->parser, buf->base, nread); if (parsed < nread) { uv_close((uv_handle_t *)&context->handle, free_context); } if (parsed != nread) { // @TODO // Error Callback } } else { if (nread != UV_EOF) { return; // maybe do something interesting here... } uv_close((uv_handle_t *)&context->handle, free_context); } free(buf->base); }; uv_buf_t reqbuf; std::string reqstr = opts.method + " " + opts.url + " HTTP/1.1" + CRLF + // // @TODO // Add user's headers here // "Connection: keep-alive" + CRLF + CRLF; reqbuf.base = (char *)reqstr.c_str(); reqbuf.len = reqstr.size(); uv_read_start( req->handle, [](uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { *buf = uv_buf_init((char *)malloc(suggested_size), suggested_size); }, [](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) { read(tcp, nread, buf); }); uv_write( &context->write_req, req->handle, &reqbuf, 1, nullptr); } void Client::connect() { addrinfo ai = { .ai_flags = 0, .ai_family = PF_INET, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, }; loop_ = uv_default_loop(); static std::function on_resolved; static std::function on_before_connect; on_before_connect = [&](uv_connect_t *req, int status) { // @TODO // Populate address and time info for logging / stats etc. on_connect(req, status); }; on_resolved = [&](uv_getaddrinfo_t *req, int status, struct addrinfo *res) { static llhttp_settings_t settings; attachEvents(this, settings); char addr[17] = {'\0'}; uv_ip4_name((struct sockaddr_in *)res->ai_addr, addr, 16); uv_freeaddrinfo(res); sockaddr_in dest = {}; uv_ip4_addr(addr, opts.port, &dest); auto *context = new Context(); context->handle.data = context; llhttp_init(&context->parser, HTTP_RESPONSE, &settings); context->parser.data = context; uv_tcp_init(loop_, &context->handle); //uv_tcp_keepalive(&context->handle, 1, 60); uv_tcp_connect( &context->connect_req, &context->handle, (const struct sockaddr *)&dest, [](uv_connect_t *req, int status) { on_before_connect(req, status); }); }; auto cb = [](uv_getaddrinfo_t *req, int status, struct addrinfo *res) { on_resolved(req, status, res); }; uv_getaddrinfo(loop_, &addr_req, cb, opts.host.c_str(), std::to_string(opts.port).c_str(), &ai); uv_run(loop_, UV_RUN_DEFAULT); } } ================================================ FILE: d2mapapi/simphttp/simphttp.h ================================================ #pragma once #include "uri.h" #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #endif #define MAX_WRITE_HANDLES 1000 #define ASSERT_STATUS(status, msg) \ if (status != 0) { \ std::cerr << msg << ": " << uv_err_name(status); \ exit(1); \ } namespace simphttp { template class Buffer; template class IStream; class Request; class Response; class Client; class Context; extern const std::string CRLF; extern void free_context(uv_handle_t *); template extern void attachEvents(Type *instance, llhttp_settings_t &settings); template class Buffer : public std::stringbuf { friend class Request; friend class Response; Type *stream; explicit Buffer(std::ostream &str) {}; ~Buffer() override = default; int sync() override { std::string out = str(); std::ostringstream buf; buf << out; out = buf.str(); stream->writeOrEnd(out, true); buf.flush(); str(""); return 0; } }; class Request { public: std::string url; std::string method; std::string status_code; std::stringstream body; std::map headers; Request() = default; ~Request() = default; }; class Response : public std::ostream { friend class Buffer; friend class Server; std::stringstream stream; Buffer buffer; void writeOrEnd(const std::string &, bool); int write_count = 0; bool writtenOrEnded = false; bool ended = false; bool headersSet = false; bool statusSet = false; bool contentLengthSet = false; public: llhttp_t parser = {}; int statusCode = 200; std::string body; std::string statusAdjective = "OK"; std::map headers; void setHeader(const std::string &, const std::string &); void setStatus(int); void setStatus(int, const std::string &); void write(const std::string &); void end(const std::string &); void end(); Response() : std::ostream(&buffer), buffer(stream) { buffer.stream = this; } ~Response() override = default; }; /* // @TODO // Maybe have each op call write // inline Response &operator << (Response &res, const std::string &s) { res.write(s); return res; }*/ class Context : public Request { public: std::map writes; uv_tcp_t handle; uv_connect_t connect_req; uv_write_t write_req; llhttp_t parser; }; class Client { template friend void attachEvents(Type *instance, llhttp_settings_t &settings); friend class Response; private: typedef std::function Listener; Listener listener; uv_loop_t *loop_ = nullptr; uv_tcp_t socket_ = {}; void connect(); int complete(llhttp_t *parser, const Listener &fn); void on_connect(uv_connect_t *req, int status); protected: uv_getaddrinfo_t addr_req; uv_shutdown_t shutdown_req; public: struct Options { std::string host = "localhost"; int port = 80; std::string method = "GET"; std::string url = "/"; }; Options opts; Client(Options o, Listener listener); Client(std::string u, Listener listener); ~Client() = default; }; class Server { template friend void attachEvents(Type *instance, llhttp_settings_t &settings); friend class Response; private: typedef std::function Listener; Listener listener; uv_loop_t *loop_ = nullptr; uv_tcp_t socket_ = {}; int complete(llhttp_t *parser, const Listener &fn); public: explicit Server(Listener listener); ~Server() = default; int listen(const char *, int); }; } // namespace http ================================================ FILE: d2mapapi/simphttp/uri.h ================================================ #ifndef NODEUV_URI #define NODEUV_URI #include #include #include #include #include #include namespace uri { using namespace std; typedef map qsmap; typedef vector split; struct url { string protocol; string user; string password; string host; string path; string search; qsmap query; int port = 0; }; //--- Helper Functions -------------------------------------------------------------~ static inline string TailSlice(string &subject, const string &delimiter, bool keep_delim = false) { // Chops off the delimiter and everything that follows (destructively) // returns everything after the delimiter auto delimiter_location = subject.find(delimiter); auto delimiter_length = delimiter.length(); string output; if (delimiter_location < string::npos) { auto start = keep_delim ? delimiter_location : delimiter_location + delimiter_length; auto end = subject.length() - start; output = subject.substr(start, end); subject = subject.substr(0, delimiter_location); } return output; } static inline string HeadSlice(string &subject, const string &delimiter) { // Chops off the delimiter and everything that precedes (destructively) // returns everthing before the delimeter auto delimiter_location = subject.find(delimiter); auto delimiter_length = delimiter.length(); string output; if (delimiter_location < string::npos) { output = subject.substr(0, delimiter_location); subject = subject.substr( delimiter_location + delimiter_length, subject.length() - (delimiter_location + delimiter_length)); } return output; } static inline split Split(const std::string &input, const string &separators, bool remove_empty = true) { split list; ostringstream word; for (char n : input) { if (string::npos == separators.find(n)) word << n; else { if (!word.str().empty() || !remove_empty) { list.push_back(word.str()); } word.str(""); } } if (!word.str().empty() || !remove_empty) { list.push_back(word.str()); } return list; } //--- Extractors -------------------------------------------------------------------~ static inline int ExtractPort(string &hostport) { int port; string portstring = TailSlice(hostport, ":"); try { port = int(strtol(portstring.c_str(), nullptr, 0)); } catch (const exception &e) { port = -1; } return port; } static inline string ExtractPath(string &in) { return TailSlice(in, "/", true); } static inline string ExtractProtocol(string &in) { return HeadSlice(in, "://"); } static inline string ExtractSearch(string &in) { return TailSlice(in, "?"); } static inline string ExtractPassword(string &userpass) { return TailSlice(userpass, ":"); } static inline string ExtractUserpass(string &in) { return HeadSlice(in, "@"); } //--- Public Interface -------------------------------------------------------------~ static inline url ParseHttpUrl(string &in) { url ret; ret.port = -1; ret.search = ExtractSearch(in); ret.protocol = ExtractProtocol(in); ret.path = ExtractPath(in); string userpass = ExtractUserpass(in); ret.password = ExtractPassword(userpass); ret.user = userpass; ret.port = ExtractPort(in); ret.host = in; auto pairs = Split(ret.search, "&"); for (auto &p: pairs) { auto pair = Split(p, "="); if (pair.size() == 2) { ret.query.insert({pair[0], pair[1]}); } } return ret; } const char HEX2DEC[256] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ /* 0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 1 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 2 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 4 */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 5 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 6 */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 7 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 8 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 9 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static inline std::string UriDecode(const std::string &sSrc) { // Note from RFC1630: "Sequences which start with a percent sign // but are not followed by two hexadecimal characters (0-9, A-F) are reserved // for future extension" const auto *pSrc = (const unsigned char *)sSrc.c_str(); const int SRC_LEN = sSrc.length(); const unsigned char *const SRC_END = pSrc + SRC_LEN; const unsigned char *const SRC_LAST_DEC = SRC_END - 2; // last decodable '%' char *const pStart = new char[SRC_LEN]; char *pEnd = pStart; while (pSrc < SRC_LAST_DEC) { if (*pSrc == '%') { char dec1, dec2; if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)]) && -1 != (dec2 = HEX2DEC[*(pSrc + 2)])) { *pEnd++ = (dec1 << 4) + dec2; pSrc += 3; continue; } } *pEnd++ = *pSrc++; } // the last 2- chars while (pSrc < SRC_END) { *pEnd++ = *pSrc++; } std::string sResult(pStart, pEnd); delete[] pStart; return sResult; } // Only alphanum is safe. const char SAFE[256] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 4 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 5 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 6 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static inline std::string UriEncode(const std::string &sSrc) { const char DEC2HEX[16 + 1] = "0123456789ABCDEF"; const auto *pSrc = (const unsigned char *)sSrc.c_str(); const int SRC_LEN = sSrc.length(); auto *const pStart = new unsigned char[SRC_LEN * 3]; auto *pEnd = pStart; const auto *const SRC_END = pSrc + SRC_LEN; for (; pSrc < SRC_END; ++pSrc) { if (SAFE[*pSrc]) { *pEnd++ = *pSrc; } else { // escape this char *pEnd++ = '%'; *pEnd++ = DEC2HEX[*pSrc >> 4]; *pEnd++ = DEC2HEX[*pSrc & 0x0F]; } } std::string sResult((char *)pStart, (char *)pEnd); delete[] pStart; return sResult; } } #endif ================================================ FILE: d2mapapi/stb/stb_image_write.h ================================================ /* stb_image_write - v1.16 - public domain - http://nothings.org/stb writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk Before #including, #define STB_IMAGE_WRITE_IMPLEMENTATION in the file that you want to have the implementation. Will probably not work correctly with strict-aliasing optimizations. ABOUT: This header file is a library for writing images to C stdio or a callback. The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation; though providing a custom zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. This library is designed for source code compactness and simplicity, not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. You can #define STBIW_MEMMOVE() to replace memmove() You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function for PNG compression (instead of the builtin one), it must have the following signature: unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); The returned data will be freed with STBIW_FREE() (free() by default), so it must be heap allocated with STBIW_MALLOC() (malloc() by default), UNICODE: If compiling for Windows and you wish to use Unicode filenames, compile with #define STBIW_WINDOWS_UTF8 and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert Windows wchar_t filenames to utf8. USAGE: There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically There are also five equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); You can configure it with these global variables: int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. Each function returns 0 on failure and non-0 on success. The functions create an image file defined by the parameters. The image is a rectangle of pixels stored from left-to-right, top-to-bottom. Each pixel contains 'comp' channels of data stored interleaved with 8-bits per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. The *data pointer points to the first byte of the top-left-most pixel. For PNG, "stride_in_bytes" is the distance in bytes from the first byte of a row of pixels to the first byte of the next row of pixels. PNG creates output files with the same number of components as the input. The BMP format expands Y to RGB in the file format and does not output alpha. PNG supports writing rectangles of data even when the bytes storing rows of data are not consecutive in memory (e.g. sub-rectangles of a larger image), by supplying the stride between the beginning of adjacent rows. The other formats do not. (Thus you cannot write a native-format BMP through the BMP writer, both because it is in BGR order and because it may have padding at the end of the line.) PNG allows you to set the deflate compression level by setting the global variable 'stbi_write_png_compression_level' (it defaults to 8). HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. JPEG does ignore alpha channels in input data; quality is between 1 and 100. Higher quality looks better but results in a bigger image. JPEG baseline (no JPEG progressive). CREDITS: Sean Barrett - PNG/BMP/TGA Baldur Karlsson - HDR Jean-Sebastien Guay - TGA monochrome Tim Kelsey - misc enhancements Alan Hickman - TGA RLE Emmanuel Julien - initial file IO callback implementation Jon Olick - original jo_jpeg.cpp code Daniel Gibson - integrate JPEG, allow external zlib Aarni Koskela - allow choosing PNG filter bugfixes: github:Chribba Guillaume Chereau github:jry2 github:romigrou Sergio Gonzalez Jonas Karlsson Filip Wasil Thatcher Ulrich github:poppolopoppo Patrick Boettcher github:xeekworx Cap Petschulat Simon Rodriguez Ivan Tikhonov github:ignotion Adam Schackart Andrew Kensler LICENSE See end of file for license information. */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H #include // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' #ifndef STBIWDEF #ifdef STB_IMAGE_WRITE_STATIC #define STBIWDEF static #else #ifdef __cplusplus #define STBIWDEF extern "C" #else #define STBIWDEF extern #endif #endif #endif #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations STBIWDEF int stbi_write_tga_with_rle; STBIWDEF int stbi_write_png_compression_level; STBIWDEF int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #ifdef STBIW_WINDOWS_UTF8 STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif #endif typedef void stbi_write_func(void *context, void *data, int size); STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #endif//INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif #ifndef STBI_WRITE_NO_STDIO #include #endif // STBI_WRITE_NO_STDIO #include #include #include #include #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) // ok #else #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." #endif #ifndef STBIW_MALLOC #define STBIW_MALLOC(sz) malloc(sz) #define STBIW_REALLOC(p,newsz) realloc(p,newsz) #define STBIW_FREE(p) free(p) #endif #ifndef STBIW_REALLOC_SIZED #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) #endif #ifndef STBIW_MEMMOVE #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) #endif #ifndef STBIW_ASSERT #include #define STBIW_ASSERT(x) assert(x) #endif #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) #ifdef STB_IMAGE_WRITE_STATIC static int stbi_write_png_compression_level = 8; static int stbi_write_tga_with_rle = 1; static int stbi_write_force_png_filter = -1; #else int stbi_write_png_compression_level = 8; int stbi_write_tga_with_rle = 1; int stbi_write_force_png_filter = -1; #endif static int stbi__flip_vertically_on_write = 0; STBIWDEF void stbi_flip_vertically_on_write(int flag) { stbi__flip_vertically_on_write = flag; } typedef struct { stbi_write_func *func; void *context; unsigned char buffer[64]; int buf_used; } stbi__write_context; // initialize a callback-based context static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) { s->func = c; s->context = context; } #ifndef STBI_WRITE_NO_STDIO static void stbi__stdio_write(void *context, void *data, int size) { fwrite(data,1,size,(FILE*) context); } #if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) #ifdef __cplusplus #define STBIW_EXTERN extern "C" #else #define STBIW_EXTERN extern #endif STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbiw__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; #if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else f = fopen(filename, mode); #endif return f; } static int stbi__start_write_file(stbi__write_context *s, const char *filename) { FILE *f = stbiw__fopen(filename, "wb"); stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } static void stbi__end_write_file(stbi__write_context *s) { fclose((FILE *)s->context); } #endif // !STBI_WRITE_NO_STDIO typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { case ' ': break; case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); s->func(s->context,&x,1); break; } case '2': { int x = va_arg(v,int); unsigned char b[2]; b[0] = STBIW_UCHAR(x); b[1] = STBIW_UCHAR(x>>8); s->func(s->context,b,2); break; } case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; b[0]=STBIW_UCHAR(x); b[1]=STBIW_UCHAR(x>>8); b[2]=STBIW_UCHAR(x>>16); b[3]=STBIW_UCHAR(x>>24); s->func(s->context,b,4); break; } default: STBIW_ASSERT(0); return; } } } static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); } static void stbiw__write_flush(stbi__write_context *s) { if (s->buf_used) { s->func(s->context, &s->buffer, s->buf_used); s->buf_used = 0; } } static void stbiw__putc(stbi__write_context *s, unsigned char c) { s->func(s->context, &c, 1); } static void stbiw__write1(stbi__write_context *s, unsigned char a) { if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) stbiw__write_flush(s); s->buffer[s->buf_used++] = a; } static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { int n; if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) stbiw__write_flush(s); n = s->buf_used; s->buf_used = n+3; s->buffer[n+0] = a; s->buffer[n+1] = b; s->buffer[n+2] = c; } static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) { unsigned char bg[3] = { 255, 0, 255}, px[3]; int k; if (write_alpha < 0) stbiw__write1(s, d[comp - 1]); switch (comp) { case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case case 1: if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp else stbiw__write1(s, d[0]); // monochrome TGA break; case 4: if (!write_alpha) { // composite against pink background for (k = 0; k < 3; ++k) px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); break; } /* FALLTHROUGH */ case 3: stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); break; } if (write_alpha > 0) stbiw__write1(s, d[comp - 1]); } static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) { stbiw_uint32 zero = 0; int i,j, j_end; if (y <= 0) return; if (stbi__flip_vertically_on_write) vdir *= -1; if (vdir < 0) { j_end = -1; j = y-1; } else { j_end = y; j = 0; } for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } stbiw__write_flush(s); s->func(s->context, &zero, scanline_pad); } } static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) { if (y < 0 || x < 0) { return 0; } else { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); return 1; } } static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { if (comp != 4) { // write RGB bitmap int pad = (-x*3) & 3; return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, "11 4 22 4" "4 44 22 444444", 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header } else { // RGBA bitmaps need a v4 header // use BI_BITFIELDS mode with 32bpp and alpha mask // (straight BI_RGB with alpha mask doesn't work in most readers) return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header } } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_bmp_core(&s, x, y, comp, data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_bmp_core(&s, x, y, comp, data); stbi__end_write_file(&s); return r; } else return 0; } #endif //!STBI_WRITE_NO_STDIO static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { int has_alpha = (comp == 2 || comp == 4); int colorbytes = has_alpha ? comp-1 : comp; int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 if (y < 0 || x < 0) return 0; if (!stbi_write_tga_with_rle) { return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); } else { int i,j,k; int jend, jdir; stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); if (stbi__flip_vertically_on_write) { j = 0; jend = y; jdir = 1; } else { j = y-1; jend = -1; jdir = -1; } for (; j != jend; j += jdir) { unsigned char *row = (unsigned char *) data + j * x * comp; int len; for (i = 0; i < x; i += len) { unsigned char *begin = row + i * comp; int diff = 1; len = 1; if (i < x - 1) { ++len; diff = memcmp(begin, row + (i + 1) * comp, comp); if (diff) { const unsigned char *prev = begin; for (k = i + 2; k < x && len < 128; ++k) { if (memcmp(prev, row + k * comp, comp)) { prev += comp; ++len; } else { --len; break; } } } else { for (k = i + 2; k < x && len < 128; ++k) { if (!memcmp(begin, row + k * comp, comp)) { ++len; } else { break; } } } } if (diff) { unsigned char header = STBIW_UCHAR(len - 1); stbiw__write1(s, header); for (k = 0; k < len; ++k) { stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); } } else { unsigned char header = STBIW_UCHAR(len - 129); stbiw__write1(s, header); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); } } } stbiw__write_flush(s); } return 1; } STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_tga_core(&s, x, y, comp, (void *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) #ifndef STBI_WRITE_NO_STDIO static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); if (maxcomp < 1e-32f) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; rgbe[0] = (unsigned char)(linear[0] * normalize); rgbe[1] = (unsigned char)(linear[1] * normalize); rgbe[2] = (unsigned char)(linear[2] * normalize); rgbe[3] = (unsigned char)(exponent + 128); } } static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); s->func(s->context, &lengthbyte, 1); s->func(s->context, &databyte, 1); } static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code s->func(s->context, &lengthbyte, 1); s->func(s->context, data, length); } static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; float linear[3]; int x; scanlineheader[2] = (width&0xff00)>>8; scanlineheader[3] = (width&0x00ff); /* skip RLE for images too small or large */ if (width < 8 || width >= 32768) { for (x=0; x < width; x++) { switch (ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); s->func(s->context, rgbe, 4); } } else { int c,r; /* encode into scratch buffer */ for (x=0; x < width; x++) { switch(ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); scratch[x + width*0] = rgbe[0]; scratch[x + width*1] = rgbe[1]; scratch[x + width*2] = rgbe[2]; scratch[x + width*3] = rgbe[3]; } s->func(s->context, scanlineheader, 4); /* RLE each component separately */ for (c=0; c < 4; c++) { unsigned char *comp = &scratch[width*c]; x = 0; while (x < width) { // find first run r = x; while (r+2 < width) { if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) break; ++r; } if (r+2 >= width) r = width; // dump up to first run while (x < r) { int len = r-x; if (len > 128) len = 128; stbiw__write_dump_data(s, len, &comp[x]); x += len; } // if there's a run, output it if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd // find next byte after run while (r < width && comp[r] == comp[x]) ++r; // output run up to r while (x < r) { int len = r-x; if (len > 127) len = 127; stbiw__write_run_data(s, len, comp[x]); x += len; } } } } } } static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { if (y <= 0 || x <= 0 || data == NULL) return 0; else { // Each component is stored separately. Allocate scratch space for full output scanline. unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); int i, len; char buffer[128]; char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); #ifdef __STDC_LIB_EXT1__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #endif s->func(s->context, buffer, len); for(i=0; i < y; i++) stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); STBIW_FREE(scratch); return 1; } } STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // STBI_WRITE_NO_STDIO ////////////////////////////////////////////////////////////////////////////// // // PNG writer // #ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (void *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] #define stbiw__sbn(a) stbiw__sbraw(a)[1] #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); STBIW_ASSERT(p); if (p) { if (!*arr) ((int *) p)[1] = 0; *arr = (void *) ((int *) p + 2); stbiw__sbm(*arr) = m; } return *arr; } static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { while (*bitcount >= 8) { stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); *bitbuffer >>= 8; *bitcount -= 8; } return data; } static int stbiw__zlib_bitrev(int code, int codebits) { int res=0; while (codebits--) { res = (res << 1) | (code & 1); code >>= 1; } return res; } static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) { int i; for (i=0; i < limit && i < 258; ++i) if (a[i] != b[i]) break; return i; } static unsigned int stbiw__zhash(unsigned char *data) { stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) #define stbiw__zlib_add(code,codebits) \ (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) // default huffman tables #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) #define stbiw__ZHASH 16384 #endif // STBIW_ZLIB_COMPRESS STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { #ifdef STBIW_ZLIB_COMPRESS // user provided a zlib compress implementation, use that return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); #else // use builtin static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); if (hash_table == NULL) return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window stbiw__sbpush(out, 0x5e); // FLEVEL = 1 stbiw__zlib_add(1,1); // BFINAL = 1 stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman for (i=0; i < stbiw__ZHASH; ++i) hash_table[i] = NULL; i=0; while (i < data_len-3) { // hash next 3 bytes of data to be compressed int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; unsigned char *bestloc = 0; unsigned char **hlist = hash_table[h]; int n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32768) { // if entry lies within window int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); if (d >= best) { best=d; bestloc=hlist[j]; } } } // when hash table entry is too long, delete half the entries if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); stbiw__sbn(hash_table[h]) = quality; } stbiw__sbpush(hash_table[h],data+i); if (bestloc) { // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); hlist = hash_table[h]; n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32767) { int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); if (e > best) { // if next match is better, bail on current match bestloc = NULL; break; } } } } if (bestloc) { int d = (int) (data+i - bestloc); // distance back STBIW_ASSERT(d <= 32767 && best <= 258); for (j=0; best > lengthc[j+1]-1; ++j); stbiw__zlib_huff(j+257); if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); for (j=0; d > distc[j+1]-1; ++j); stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); i += best; } else { stbiw__zlib_huffb(data[i]); ++i; } } // write out final bytes for (;i < data_len; ++i) stbiw__zlib_huffb(data[i]); stbiw__zlib_huff(256); // end of block // pad with 0 bits to byte boundary while (bitcount) stbiw__zlib_add(0,1); for (i=0; i < stbiw__ZHASH; ++i) (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); // store uncompressed instead if compression was worse if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 for (j = 0; j < data_len;) { int blocklen = data_len - j; if (blocklen > 32767) blocklen = 32767; stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); memcpy(out+stbiw__sbn(out), data+j, blocklen); stbiw__sbn(out) += blocklen; j += blocklen; } } { // compute adler32 on input unsigned int s1=1, s2=0; int blocklen = (int) (data_len % 5552); j=0; while (j < data_len) { for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } s1 %= 65521; s2 %= 65521; j += blocklen; blocklen = 5552; } stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s2)); stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s1)); } *out_len = stbiw__sbn(out); // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); #endif // STBIW_ZLIB_COMPRESS } static unsigned int stbiw__crc32(unsigned char *buffer, int len) { #ifdef STBIW_CRC32 return STBIW_CRC32(buffer, len); #else static unsigned int crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; unsigned int crc = ~0u; int i; for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; #endif } #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) static void stbiw__wpcrc(unsigned char **data, int len) { unsigned int crc = stbiw__crc32(*data - len - 4, len+4); stbiw__wp32(*data, crc); } static unsigned char stbiw__paeth(int a, int b, int c) { int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); if (pb <= pc) return STBIW_UCHAR(b); return STBIW_UCHAR(c); } // @OPTIMIZE: provide an option that always forces left-predict or paeth predict static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) { static int mapping[] = { 0,1,2,3,4 }; static int firstmap[] = { 0,1,0,5,6 }; int *mymap = (y != 0) ? mapping : firstmap; int i; int type = mymap[filter_type]; unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; if (type==0) { memcpy(line_buffer, z, width*n); return; } // first loop isn't optimized since it's just one pixel for (i = 0; i < n; ++i) { switch (type) { case 1: line_buffer[i] = z[i]; break; case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; case 5: line_buffer[i] = z[i]; break; case 6: line_buffer[i] = z[i]; break; } } switch (type) { case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; } } STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; if (force_filter >= 5) { force_filter = -1; } filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { int filter_type; if (force_filter > -1) { filter_type = force_filter; stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); } else { // Estimate the best filter by running through all of them: int best_filter = 0, best_filter_val = 0x7fffffff, est, i; for (filter_type = 0; filter_type < 5; filter_type++) { stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); // Estimate the entropy of the line using this filter; the less, the better. est = 0; for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); } if (est < best_filter_val) { best_filter_val = est; best_filter = filter_type; } } if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); filter_type = best_filter; } } // when we get here, filter_type contains the filter type, and line_buffer contains the data filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; // each tag requires 12 bytes of overhead out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); if (!out) return 0; *out_len = 8 + 12+13 + 12+zlen + 12; o=out; STBIW_MEMMOVE(o,sig,8); o+= 8; stbiw__wp32(o, 13); // header length stbiw__wptag(o, "IHDR"); stbiw__wp32(o, x); stbiw__wp32(o, y); *o++ = 8; *o++ = STBIW_UCHAR(ctype[n]); *o++ = 0; *o++ = 0; *o++ = 0; stbiw__wpcrc(&o,13); stbiw__wp32(o, zlen); stbiw__wptag(o, "IDAT"); STBIW_MEMMOVE(o, zlib, zlen); o += zlen; STBIW_FREE(zlib); stbiw__wpcrc(&o, zlen); stbiw__wp32(o,0); stbiw__wptag(o, "IEND"); stbiw__wpcrc(&o,0); STBIW_ASSERT(o == out + *out_len); return out; } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { FILE *f; int len; unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; f = stbiw__fopen(filename, "wb"); if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); STBIW_FREE(png); return 1; } #endif STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) { int len; unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; func(context, png, len); STBIW_FREE(png); return 1; } /* *************************************************************************** * * JPEG writer * * This is based on Jon Olick's jo_jpeg.cpp: * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html */ static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { int bitBuf = *bitBufP, bitCnt = *bitCntP; bitCnt += bs[1]; bitBuf |= bs[0] << (24 - bitCnt); while(bitCnt >= 8) { unsigned char c = (bitBuf >> 16) & 255; stbiw__putc(s, c); if(c == 255) { stbiw__putc(s, 0); } bitBuf <<= 8; bitCnt -= 8; } *bitBufP = bitBuf; *bitCntP = bitCnt; } static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; float z1, z2, z3, z4, z5, z11, z13; float tmp0 = d0 + d7; float tmp7 = d0 - d7; float tmp1 = d1 + d6; float tmp6 = d1 - d6; float tmp2 = d2 + d5; float tmp5 = d2 - d5; float tmp3 = d3 + d4; float tmp4 = d3 - d4; // Even part float tmp10 = tmp0 + tmp3; // phase 2 float tmp13 = tmp0 - tmp3; float tmp11 = tmp1 + tmp2; float tmp12 = tmp1 - tmp2; d0 = tmp10 + tmp11; // phase 3 d4 = tmp10 - tmp11; z1 = (tmp12 + tmp13) * 0.707106781f; // c4 d2 = tmp13 + z1; // phase 5 d6 = tmp13 - z1; // Odd part tmp10 = tmp4 + tmp5; // phase 2 tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; // The rotator is modified from fig 4-8 to avoid extra negations. z5 = (tmp10 - tmp12) * 0.382683433f; // c6 z2 = tmp10 * 0.541196100f + z5; // c2-c6 z4 = tmp12 * 1.306562965f + z5; // c2+c6 z3 = tmp11 * 0.707106781f; // c4 z11 = tmp7 + z3; // phase 5 z13 = tmp7 - z3; *d5p = z13 + z2; // phase 6 *d3p = z13 - z2; *d1p = z11 + z4; *d7p = z11 - z4; *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; } static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { int tmp1 = val < 0 ? -val : val; val = val < 0 ? val-1 : val; bits[1] = 1; while(tmp1 >>= 1) { ++bits[1]; } bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { } // end0pos = first element in reverse order !=0 if(end0pos == 0) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); return DU[0]; } for(i = 1; i <= end0pos; ++i) { int startpos = i; int nrzeroes; unsigned short bits[2]; for (; DU[i]==0 && i<=end0pos; ++i) { } nrzeroes = i-startpos; if ( nrzeroes >= 16 ) { int lng = nrzeroes>>4; int nrmarker; for (nrmarker=1; nrmarker <= lng; ++nrmarker) stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); nrzeroes &= 15; } stbiw__jpg_calcBits(DU[i], bits); stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); } if(end0pos != 63) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); } return DU[0]; } static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { // Constants that don't pollute global namespace static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; static const unsigned char std_ac_luminance_values[] = { 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; static const unsigned char std_ac_chrominance_values[] = { 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; // Huffman tables static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; static const unsigned short YAC_HT[256][2] = { {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const unsigned short UVAC_HT[256][2] = { {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; int row, col, i, k, subsample; float fdtbl_Y[64], fdtbl_UV[64]; unsigned char YTable[64], UVTable[64]; if(!data || !width || !height || comp > 4 || comp < 1) { return 0; } quality = quality ? quality : 90; subsample = quality <= 90 ? 1 : 0; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; for(i = 0; i < 64; ++i) { int uvti, yti = (YQT[i]*quality+50)/100; YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); uvti = (UVQT[i]*quality+50)/100; UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); } for(row = 0, k = 0; row < 8; ++row) { for(col = 0; col < 8; ++col, ++k) { fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); } } // Write Headers { static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; s->func(s->context, (void*)head0, sizeof(head0)); s->func(s->context, (void*)YTable, sizeof(YTable)); stbiw__putc(s, 1); s->func(s->context, UVTable, sizeof(UVTable)); s->func(s->context, (void*)head1, sizeof(head1)); s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); stbiw__putc(s, 0x10); // HTYACinfo s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); stbiw__putc(s, 1); // HTUDCinfo s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); stbiw__putc(s, 0x11); // HTUACinfo s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); s->func(s->context, (void*)head2, sizeof(head2)); } // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; int DCY=0, DCU=0, DCV=0; int bitBuf=0, bitCnt=0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; const unsigned char *dataR = (const unsigned char *)data; const unsigned char *dataG = dataR + ofsG; const unsigned char *dataB = dataR + ofsB; int x, y, pos; if(subsample) { for(y = 0; y < height; y += 16) { for(x = 0; x < width; x += 16) { float Y[256], U[256], V[256]; for(row = y, pos = 0; row < y+16; ++row) { // row >= height => use last input row int clamped_row = (row < height) ? row : height - 1; int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; for(col = x; col < x+16; ++col, ++pos) { // if col >= width => use pixel from last input column int p = base_p + ((col < width) ? col : (width-1))*comp; float r = dataR[p], g = dataG[p], b = dataB[p]; Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; } } DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); // subsample U,V { float subU[64], subV[64]; int yy, xx; for(yy = 0, pos = 0; yy < 8; ++yy) { for(xx = 0; xx < 8; ++xx, ++pos) { int j = yy*32+xx*2; subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; } } DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } } } else { for(y = 0; y < height; y += 8) { for(x = 0; x < width; x += 8) { float Y[64], U[64], V[64]; for(row = y, pos = 0; row < y+8; ++row) { // row >= height => use last input row int clamped_row = (row < height) ? row : height - 1; int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; for(col = x; col < x+8; ++col, ++pos) { // if col >= width => use pixel from last input column int p = base_p + ((col < width) ? col : (width-1))*comp; float r = dataR[p], g = dataG[p], b = dataB[p]; Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; } } DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } } // Do the bit alignment of the EOI marker stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); } // EOI stbiw__putc(s, 0xFF); stbiw__putc(s, 0xD9); return 1; } STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); stbi__end_write_file(&s); return r; } else return 0; } #endif #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history 1.16 (2021-07-11) make Deflate code emit uncompressed blocks when it would otherwise expand support writing BMPs with alpha channel 1.15 (2020-07-13) unknown 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels 1.13 1.12 1.11 (2019-08-11) 1.10 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 1.09 (2018-02-11) fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 1.08 (2018-01-29) add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1.07 (2017-07-24) doc fix 1.06 (2017-07-23) writing JPEG (using Jon Olick's code) 1.05 ??? 1.04 (2017-03-03) monochrome BMP expansion 1.03 ??? 1.02 (2016-04-02) avoid allocating large structures on the stack 1.01 (2016-01-16) STBIW_REALLOC_SIZED: support allocators with no realloc support avoid race-condition in crc initialization minor compile issues 1.00 (2015-09-14) installable file IO function 0.99 (2015-09-13) warning fixes; TGA rle support 0.98 (2015-04-08) added STBIW_MALLOC, STBIW_ASSERT etc 0.97 (2015-01-18) fixed HDR asserts, rewrote HDR rle logic 0.96 (2015-01-17) add HDR output fix monochrome BMP 0.95 (2014-08-17) add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) warning fixes 0.92 (2010-08-01) casts to unsigned char to fix warnings 0.91 (2010-07-17) first public release 0.90 first internal release */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ================================================ FILE: deps/CMakeLists.txt ================================================ add_subdirectory(glad) add_subdirectory(stb) add_subdirectory(inih) add_subdirectory(CascLib) find_package(Git REQUIRED QUIET) include(ExternalProject) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(LUA54_LIB_SUFFIX "d") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(LUA54_INSTALL_COMMANDS INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /bin64 ${CMAKE_BINARY_DIR}/bin COMMAND ${CMAKE_COMMAND} -E copy_directory /include ${CMAKE_BINARY_DIR}/include/lua COMMAND ${CMAKE_COMMAND} -E copy_directory /lib64 ${CMAKE_BINARY_DIR}/lib ) else() set(LUA54_INSTALL_COMMANDS INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /bin ${CMAKE_BINARY_DIR}/bin COMMAND ${CMAKE_COMMAND} -E copy_directory /include ${CMAKE_BINARY_DIR}/include/lua COMMAND ${CMAKE_COMMAND} -E copy_directory /lib ${CMAKE_BINARY_DIR}/lib ) endif() ExternalProject_Add(lua54 PREFIX ${CMAKE_CURRENT_BINARY_DIR}/lua54 GIT_REPOSITORY https://github.com/NLua/lua.git GIT_TAG main GIT_SHALLOW ON EXCLUDE_FROM_ALL ON UPDATE_DISCONNECTED ON STEP_TARGETS update CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DLUA_BUILD_AS_DLL=OFF ${LUA54_INSTALL_COMMANDS} ) add_library(lua54-static INTERFACE) add_dependencies(lua54-static lua54) target_include_directories(lua54-static INTERFACE ${CMAKE_BINARY_DIR}/include/lua) target_link_directories(lua54-static INTERFACE ${CMAKE_BINARY_DIR}/lib) target_link_libraries(lua54-static INTERFACE lua54_static${LUA54_LIB_SUFFIX}) add_library(lua::lua54 ALIAS lua54-static) list(APPEND EXTERNAL_PROJECTS lua54) add_subdirectory(sol3) # update external project add_custom_command(OUTPUT ${PROJECT_NAME}-reset_projects COMMAND "") add_custom_target(${PROJECT_NAME}-update_deps DEPENDS simpio-update_deps ${PROJECT_NAME}-reset_projects) foreach(PROJ ${EXTERNAL_PROJECTS}) add_dependencies(${PROJECT_NAME}-update_deps ${PROJ}-update) ExternalProject_Get_property(${PROJ} STAMP_DIR) add_custom_command(OUTPUT ${PROJECT_NAME}-reset_projects COMMAND ${CMAKE_COMMAND} -E rm -f ${STAMP_DIR}/${PROJ}-patch ${STAMP_DIR}/${PROJ}-configure ${STAMP_DIR}/${PROJ}-build ${STAMP_DIR}/${PROJ}-install ${STAMP_DIR}/${PROJ}-done APPEND) endforeach() set_source_files_properties(${PROJECT_NAME}-reset_projects PROPERTIES SYMBOLIC "true") ================================================ FILE: deps/CascLib/CMakeLists.txt ================================================ set(HEADER_FILES src/CascCommon.h src/CascLib.h src/CascPort.h src/common/Array.h src/common/Common.h src/common/Csv.h src/common/Directory.h src/common/FileStream.h src/common/FileTree.h src/common/ListFile.h src/common/Map.h src/common/Mime.h src/common/Path.h src/common/RootHandler.h src/common/Sockets.h src/jenkins/lookup.h ) set(SRC_FILES src/common/Common.cpp src/common/Directory.cpp src/common/Csv.cpp src/common/FileStream.cpp src/common/FileTree.cpp src/common/ListFile.cpp src/common/Mime.cpp src/common/RootHandler.cpp src/common/Sockets.cpp src/jenkins/lookup3.c src/md5/md5.cpp src/CascDecompress.cpp src/CascDecrypt.cpp src/CascDumpData.cpp src/CascFiles.cpp src/CascFindFile.cpp src/CascIndexFiles.cpp src/CascOpenFile.cpp src/CascOpenStorage.cpp src/CascReadFile.cpp src/CascRootFile_Diablo3.cpp src/CascRootFile_Install.cpp src/CascRootFile_MNDX.cpp src/CascRootFile_Text.cpp src/CascRootFile_TVFS.cpp src/CascRootFile_OW.cpp src/CascRootFile_WoW.cpp ) set(ZLIB_FILES src/zlib/adler32.c src/zlib/crc32.c src/zlib/inffast.c src/zlib/inflate.c src/zlib/inftrees.c src/zlib/zutil.c ) add_library(casc STATIC EXCLUDE_FROM_ALL ${SRC_FILES} ${HEADER_FILES} ${ZLIB_FILES}) target_compile_definitions(casc PUBLIC CASCLIB_NO_AUTO_LINK_LIBRARY _UNICODE UNICODE PRIVATE STRSAFE_NO_DEPRECATE) target_include_directories(casc PUBLIC src) target_link_libraries(casc ws2_32) set_target_properties(casc PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) ================================================ FILE: deps/CascLib/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Ladislav Zezula Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: deps/CascLib/README.md ================================================ # CascLib **CascLib** is an open-source implementation of library for reading Blizzard's CASC storages since 2014. For API documentation, refer to http://www.zezula.net/en/casc/casclib.html . ## Using CascLib as static library in Windows 1. Clone the CascLib repository into a local folder: `git clone https://github.com/ladislav-zezula/CascLib.git` 2. Open one of the solution files in Microsoft Visual Studio - `CascLib_vs17.sln` for Visual Studio 2017 - `CascLib_vs15.sln` for Visual Studio 2015 - `CascLib_vs08.sln` for Visual Studio 2008 3. Select `Build / Batch Build` and select all `CascLib` build configurations. Do a full build. The result LIB files for each platform are in `.\bin\CascLib\Win32` and `.\bin\CascLib\x64`. The following build configurations are available: - DebugAD\CascLibDAD.lib (Debug Ansi version with dynamic CRT library) - DebugAS\CascLibDAS.lib (Debug Ansi version with static CRT library) - DebugUD\CascLibDUD.lib (Debug Unicode version with dynamic CRT library) - DebugUS\CascLibDUS.lib (Debug Unicode version with static CRT library) - ReleaseAD\CascLibRAD.lib (Release Ansi version with dynamic CRT library) - ReleaseAS\CascLibRAS.lib (Release Ansi version with static CRT library) - ReleaseUD\CascLibRUD.lib (Release Unicode version with dynamic CRT library) - ReleaseUS\CascLibRUS.lib (Release Unicode version with static CRT library) 4. After the build is done, put all 32-bit LIBs to a library directory (e.g. `lib32`) and all 64-bit LIBs into another directory (e.g. `lib64`) 5. Include `CascLib.h` in your project. `CascLib.h` will automatically select the required LIB file, depending on your project settings. 6. Build your project. ## Using CascLib as DLL in Windows 1. Clone the CascLib repository into a local folder: `git clone https://github.com/ladislav-zezula/CascLib.git` 2. Open one of the solution files in Microsoft Visual Studio - `CascLib_vs17.sln` for Visual Studio 2017 - `CascLib_vs15.sln` for Visual Studio 2015 - `CascLib_vs08.sln` for Visual Studio 2008 3. Select `Build / Batch Build` and check all `CascLib_dll Release` configurations. Do a full build. The result DLL and LIB files for `Win32` and `x64` platforms are in: - `.\bin\CascLib_dll\Win32\Release` (32-bit build) - `.\bin\CascLib_dll\x64\Release` (64-bit build) 5. Include `CascLib.h` and add `CascLib.lib` to your project and build it. ================================================ FILE: deps/CascLib/src/CascCommon.h ================================================ /*****************************************************************************/ /* CascCommon.h Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Common functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad The first version of CascCommon.h */ /*****************************************************************************/ #ifndef __CASCCOMMON_H__ #define __CASCCOMMON_H__ //----------------------------------------------------------------------------- // Compression support // Include functions from zlib #ifndef CASC_USE_SYSTEM_ZLIB #include "zlib/zlib.h" #else #include #endif #include "CascPort.h" #include "common/Common.h" #include "common/Array.h" #include "common/Map.h" #include "common/FileTree.h" #include "common/FileStream.h" #include "common/Directory.h" #include "common/ListFile.h" #include "common/Csv.h" #include "common/Mime.h" #include "common/Path.h" #include "common/RootHandler.h" #include "common/Sockets.h" // Headers from Alexander Peslyak's MD5 implementation #include "md5/md5.h" // For HashStringJenkins #include "jenkins/lookup.h" // On-disk CASC structures #include "CascStructs.h" //----------------------------------------------------------------------------- // CascLib private defines #ifdef _DEBUG #define BREAK_ON_XKEY3(CKey, v0, v1, v2) if(CKey[0] == v0 && CKey[1] == v1 && CKey[2] == v2) { __debugbreak(); } #define BREAKIF(condition) if(condition) { __debugbreak(); } #else #define BREAK_ON_XKEY3(CKey, v0, v1, v2) { /* NOTHING */ } #define BREAKIF(condition) { /* NOTHING */ } #endif #define CASC_MAGIC_STORAGE 0x524F545343534143 // 'CASCSTOR' #define CASC_MAGIC_FILE 0x454C494643534143 // 'CASCFILE' #define CASC_MAGIC_FIND 0x444E494643534143 // 'CASCFIND' // For CASC_CDN_DOWNLOAD::Flags #define CASC_CDN_FORCE_DOWNLOAD 0x0001 // Force downloading the file even if in the cache //----------------------------------------------------------------------------- // In-memory structures typedef enum _CBLD_TYPE { CascBuildNone = 0, // No build type found CascBuildInfo, // .build.info CascBuildDb, // .build.db (older storages) CascVersionsDb // versions (downloaded online) } CBLD_TYPE, *PCBLD_TYPE; typedef enum _CSTRTG { CascCacheInvalid, // Do not cache anything. Used as invalid value CascCacheNothing, // Do not cache anything. Used on internal files, where the content is loaded directly to the user buffer CascCacheLastFrame, // Only cache one file frame } CSTRTG, *PCSTRTG; // Tag file entry, loaded from the DOWNLOAD file typedef struct _CASC_TAG_ENTRY { USHORT HashType; // Hash type char TagName[1]; // Tag name. Variable length. } CASC_TAG_ENTRY, *PCASC_TAG_ENTRY; // Information about index file typedef struct _CASC_INDEX { LPTSTR szFileName; // Full name of the index file LPBYTE pbFileData; // Loaded content of the index file size_t cbFileData; // Size of the index file DWORD NewSubIndex; // New subindex DWORD OldSubIndex; // Old subindex } CASC_INDEX, *PCASC_INDEX; // Normalized header of the index files. // Both version 1 and version 2 are converted to this structure typedef struct _CASC_INDEX_HEADER { USHORT IndexVersion; // 5 for index v 1.0, 7 for index version 2.0 BYTE BucketIndex; // Should be the same as the first byte of the hex filename. BYTE StorageOffsetLength; // Length, in bytes, of the StorageOffset field in the EKey entry BYTE EncodedSizeLength; // Length, in bytes, of the EncodedSize in the EKey entry BYTE EKeyLength; // Length, in bytes, of the (trimmed) EKey in the EKey entry BYTE FileOffsetBits; // Number of bits of the archive file offset in StorageOffset field. Rest is data segment index BYTE Alignment; ULONGLONG SegmentSize; // Size of one data segment (aka data.### file) size_t HeaderLength; // Length of the on-disk header structure, in bytes size_t HeaderPadding; // Length of padding after the header size_t EntryLength; // Length of the on-disk EKey entry structure, in bytes size_t EKeyCount; // Number of EKey entries. Only supplied for index files version 1. } CASC_INDEX_HEADER, *PCASC_INDEX_HEADER; // Normalized footer of the archive index files (md5.index) typedef struct _CASC_ARCINDEX_FOOTER { BYTE TocHash[MD5_HASH_SIZE]; // Hash of the structure BYTE FooterHash[MD5_HASH_SIZE]; // MD5 hash of the footer, possibly shortened BYTE Version; // Version of the index footer BYTE OffsetBytes; // Length, in bytes, of the file offset field BYTE SizeBytes; // Length, in bytes, of the file size field BYTE EKeyLength; // Length, in bytes, of the EKey field BYTE FooterHashBytes; // Length, in bytes, of the hash and checksum BYTE Alignment[3]; size_t ElementCount; // total number of elements in the index file size_t PageLength; // Length, in bytes, of the index page size_t ItemLength; // Length, in bytes, of the single item size_t FooterLength; // Length, in bytes, of the index footer structure } CASC_ARCINDEX_FOOTER, *PCASC_ARCINDEX_FOOTER; // Normalized header of the ENCODING file typedef struct _CASC_ENCODING_HEADER { USHORT Magic; // FILE_MAGIC_ENCODING ('EN') USHORT Version; // Expected to be 1 by CascLib BYTE CKeyLength; // The content key length in ENCODING file. Usually 0x10 BYTE EKeyLength; // The encoded key length in ENCODING file. Usually 0x10 DWORD CKeyPageSize; // Size of the CKey page in bytes DWORD EKeyPageSize; // Size of the CKey page in bytes DWORD CKeyPageCount; // Number of CKey pages in the page table DWORD EKeyPageCount; // Number of EKey pages in the page table DWORD ESpecBlockSize; // Size of the ESpec string block, in bytes } CASC_ENCODING_HEADER, *PCASC_ENCODING_HEADER; typedef struct _CASC_DOWNLOAD_HEADER { USHORT Magic; // FILE_MAGIC_DOWNLOAD ('DL') USHORT Version; // Version USHORT EKeyLength; // Length of the EKey USHORT EntryHasChecksum; // If nonzero, then the entry has checksum DWORD EntryCount; DWORD TagCount; USHORT FlagByteSize; USHORT BasePriority; size_t HeaderLength; // Length of the on-disk header, in bytes size_t EntryLength; // Length of the on-disk entry, in bytes } CASC_DOWNLOAD_HEADER, *PCASC_DOWNLOAD_HEADER; typedef struct _CASC_DOWNLOAD_ENTRY { BYTE EKey[MD5_HASH_SIZE]; ULONGLONG EncodedSize; DWORD Checksum; DWORD Flags; BYTE Priority; } CASC_DOWNLOAD_ENTRY, *PCASC_DOWNLOAD_ENTRY; // Capturable tag structure for loading from DOWNLOAD manifest typedef struct _CASC_TAG_ENTRY1 { const char * szTagName; // Tag name LPBYTE Bitmap; // Bitmap size_t BitmapLength; // Length of the bitmap, in bytes size_t NameLength; // Length of the tag name, in bytes, not including '\0' size_t TagLength; // Length of the on-disk tag, in bytes DWORD TagValue; // Tag value } CASC_TAG_ENTRY1, *PCASC_TAG_ENTRY1; // Tag structure for storing in arrays typedef struct _CASC_TAG_ENTRY2 { size_t NameLength; // Length of the on-disk tag, in bytes DWORD TagValue; // Tag value char szTagName[0x08]; // Tag string. This member can be longer than declared. Aligned to 8 bytes. } CASC_TAG_ENTRY2, *PCASC_TAG_ENTRY2; typedef struct _CASC_INSTALL_HEADER { USHORT Magic; // FILE_MAGIC_DOWNLOAD ('DL') BYTE Version; // Version BYTE EKeyLength; // Length of the EKey DWORD EntryCount; DWORD TagCount; size_t HeaderLength; // Length of the on-disk header, in bytes } CASC_INSTALL_HEADER, *PCASC_INSTALL_HEADER; typedef struct _CASC_FILE_FRAME { CONTENT_KEY FrameHash; // MD5 hash of the file frame ULONGLONG StartOffset; // Starting offset of the file span ULONGLONG EndOffset; // Ending offset of the file span ULONGLONG DataFileOffset; // Offset in the data file (data.###) DWORD EncodedSize; // Encoded size of the frame DWORD ContentSize; // Content size of the frame } CASC_FILE_FRAME, *PCASC_FILE_FRAME; typedef struct _CASC_FILE_SPAN { ULONGLONG StartOffset; // Starting offset of the file span ULONGLONG EndOffset; // Ending offset of the file span PCASC_FILE_FRAME pFrames; TFileStream * pStream; // [Opened] stream for the file span DWORD ArchiveIndex; // Index of the archive DWORD ArchiveOffs; // Offset in the archive DWORD HeaderSize; // Size of encoded frame headers DWORD FrameCount; // Number of frames in this span } CASC_FILE_SPAN, *PCASC_FILE_SPAN; // Structure for downloading a file from the CDN (https://wowdev.wiki/TACT#File_types) // Remote path is combined as the following: // [szCdnsHost] /[szCdnsPath]/[szPathType]/EKey[0-1]/EKey[2-3]/[EKey].[Extension] // level3.blizzard.com/tpr/bnt001 /data /fe /3d /fe3d7cf9d04e07066de32bd95a5c2627.index typedef struct _CASC_CDN_DOWNLOAD { ULONGLONG ArchiveOffs; // Archive offset (if pbArchiveKey != NULL) LPCTSTR szCdnsHost; // Address of the remote CDN server. ("level3.blizzard.com") // If NULL, the downloader will try all CDN servers from the storage LPCTSTR szCdnsPath; // Remote CDN path ("tpr/bnt001") LPCTSTR szPathType; // Path type ("config", "data", "patch") LPCTSTR szLoPaType; // Local path type ("config", "data", "patch"). If NULL, it's gonna be the same like szPathType, If "", then it's not used LPCTSTR szFileName; // Plain file name, without path and extension LPBYTE pbArchiveKey; // If non-NULL, then the file is present in the archive. LPBYTE pbEKey; // 16-byte EKey of the file of of the archive LPCTSTR szExtension; // Extension for the file. Can be NULL. LPTSTR szLocalPath; // Pointer to the variable that, upon success, reveives the local path where the file was downloaded size_t ccLocalPath; // Maximum length of szLocalPath, in TCHARs DWORD ArchiveIndex; // Index of the archive (if pbArchiveKey != NULL) DWORD EncodedSize; // Encoded length (if pbArchiveKey != NULL) DWORD Flags; // See CASC_CDN_FLAG_XXX } CASC_CDN_DOWNLOAD, *PCASC_CDN_DOWNLOAD; //----------------------------------------------------------------------------- // Structures for CASC storage and CASC file struct TCascStorage { TCascStorage(); ~TCascStorage(); TCascStorage * AddRef(); TCascStorage * Release(); static TCascStorage * IsValid(HANDLE hStorage) { TCascStorage * hs = (TCascStorage *)hStorage; return (hs != INVALID_HANDLE_VALUE && hs != NULL && hs->ClassName == CASC_MAGIC_STORAGE) ? hs : NULL; } // Class recognizer. Has constant value of 'CASCSTOR' (CASC_MAGIC_STORAGE) ULONGLONG ClassName; // Class members PCASC_OPEN_STORAGE_ARGS pArgs; // Open storage arguments. Only valid during opening the storage CASC_LOCK StorageLock; // Lock for multi-threaded operations LPCTSTR szIndexFormat; // Format of the index file name LPTSTR szCodeName; // On local storage, this select a product in a multi-product storage. For online storage, this selects a product LPTSTR szRootPath; // Path where the build file is LPTSTR szDataPath; // This is the directory where data files are LPTSTR szIndexPath; // This is the directory where index files are LPTSTR szBuildFile; // Build file name (.build.info or .build.db) LPTSTR szCdnServers; // Multi-SZ list of CDN servers LPTSTR szCdnPath; // Remote CDN sub path for the product LPSTR szRegion; // Product region. Only when "versions" is used as storage root file LPSTR szBuildKey; // Product build key, aka MD5 of the build file DWORD dwDefaultLocale; // Default locale, read from ".build.info" DWORD dwBuildNumber; // Product build number DWORD dwRefCount; // Number of references DWORD dwFeatures; // List of CASC features. See CASC_FEATURE_XXX CBLD_TYPE BuildFileType; // Type of the build file QUERY_KEY CdnConfigKey; // Currently selected CDN config file. Points to "config\%02X\%02X\%s QUERY_KEY CdnBuildKey; // Currently selected CDN build file. Points to "config\%02X\%02X\%s QUERY_KEY ArchiveGroup; // Key array of the "archive-group" QUERY_KEY ArchivesKey; // Key array of the "archives" QUERY_KEY PatchArchivesKey; // Key array of the "patch-archives" QUERY_KEY PatchArchivesGroup; // Key array of the "patch-archive-group" QUERY_KEY BuildFiles; // List of supported build files TFileStream * DataFiles[CASC_MAX_DATA_FILES]; // Array of open data files CASC_INDEX IndexFiles[CASC_INDEX_COUNT]; // Array of found index files CASC_MAP IndexEKeyMap; CASC_CKEY_ENTRY EncodingCKey; // Information about ENCODING file CASC_CKEY_ENTRY DownloadCKey; // Information about DOWNLOAD file CASC_CKEY_ENTRY InstallCKey; // Information about INSTALL file CASC_CKEY_ENTRY PatchFile; // Information about PATCH file CASC_CKEY_ENTRY RootFile; // Information about ROOT file CASC_CKEY_ENTRY SizeFile; // Information about SIZE file CASC_CKEY_ENTRY VfsRoot; // The main VFS root file CASC_ARRAY VfsRootList; // List of CASC_EKEY_ENTRY for each TVFS sub-root TRootHandler * pRootHandler; // Common handler for various ROOT file formats CASC_ARRAY IndexArray; // Array of CASC_EKEY_ENTRY, loaded from online indexes CASC_ARRAY CKeyArray; // Array of CASC_CKEY_ENTRY, loaded from ENCODING file CASC_ARRAY TagsArray; // Array of CASC_DOWNLOAD_TAG2 CASC_MAP IndexMap; // Map of EKey -> IndexArray (for online archives) CASC_MAP CKeyMap; // Map of CKey -> CKeyArray CASC_MAP EKeyMap; // Map of EKey -> CKeyArray size_t LocalFiles; // Number of files that are present locally size_t TotalFiles; // Total number of files in the storage, some may not be present locally size_t EKeyEntries; // Number of CKeyEntry-ies loaded from text build file size_t EKeyLength; // EKey length from the index files DWORD FileOffsetBits; // Number of bits in the storage offset which mean data segent offset CASC_KEY_MAP KeyMap; // Growable map of encryption keys ULONGLONG LastFailKeyName; // The value of the encryption key that recently was NOT found. }; struct TCascFile { TCascFile(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry); ~TCascFile(); DWORD OpenFileSpans(LPCTSTR szSpanList); void InitFileSpans(PCASC_FILE_SPAN pSpans, DWORD dwSpanCount); void InitCacheStrategy(); static TCascFile * IsValid(HANDLE hFile) { TCascFile * hf = (TCascFile *)hFile; return (hf != INVALID_HANDLE_VALUE && hf != NULL && hf->ClassName == CASC_MAGIC_FILE && hf->pCKeyEntry != NULL) ? hf : NULL; } // Class recognizer. Has constant value of 'CASCFILE' (CASC_MAGIC_FILE) ULONGLONG ClassName; // Class members TCascStorage * hs; // Pointer to storage structure PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the first CKey entry. Each entry describes one file span PCASC_FILE_SPAN pFileSpan; // Pointer to the first file span entry ULONGLONG ContentSize; // Content size. This is the summed content size of all file spans ULONGLONG EncodedSize; // Encoded size. This is the summed encoded size of all file spans ULONGLONG FilePointer; // Current file pointer DWORD SpanCount; // Number of file spans. There is one CKey entry for each file span DWORD bVerifyIntegrity:1; // If true, then the data are validated more strictly when read DWORD bDownloadFileIf:1; // If true, then the data will be downloaded from the online storage if missing DWORD bCloseFileStream:1; // If true, file stream needs to be closed during CascCloseFile DWORD bOvercomeEncrypted:1; // If true, then CascReadFile will fill the part that is encrypted (and key was not found) with zeros DWORD bFreeCKeyEntries:1; // If true, dectructor will free the array of CKey entries ULONGLONG FileCacheStart; // Starting offset of the file cached area ULONGLONG FileCacheEnd; // Ending offset of the file cached area LPBYTE pbFileCache; // Pointer to file cached area CSTRTG CacheStrategy; // Caching strategy. See CSTRTG enum for more info }; struct TCascSearch { TCascSearch(TCascStorage * ahs, LPCTSTR aszListFile, const char * aszMask) { // Init the class ClassName = CASC_MAGIC_FIND; hs = ahs->AddRef(); // Init provider-specific data pCache = NULL; nFileIndex = 0; nSearchState = 0; bListFileUsed = false; // Allocate mask szListFile = CascNewStr(aszListFile); szMask = CascNewStr((aszMask != NULL) ? aszMask : "*"); } ~TCascSearch() { // Dereference the CASC storage hs = hs->Release(); ClassName = 0; // Free the rest of the members CASC_FREE(szMask); CASC_FREE(szListFile); CASC_FREE(pCache); } static TCascSearch * IsValid(HANDLE hFind) { TCascSearch * pSearch = (TCascSearch *)hFind; return (hFind != INVALID_HANDLE_VALUE && hFind != NULL && pSearch->ClassName == CASC_MAGIC_FIND && pSearch->szMask != NULL) ? pSearch : NULL; } ULONGLONG ClassName; // Contains 'CASCFIND' TCascStorage * hs; // Pointer to the storage handle LPTSTR szListFile; // Name of the listfile void * pCache; // Listfile cache char * szMask; // Search mask // Provider-specific data size_t nFileIndex; // Root-specific search context DWORD nSearchState:8; // The current search state (0 = listfile, 1 = nameless, 2 = done) DWORD bListFileUsed:1; // TRUE: The listfile has already been loaded }; //----------------------------------------------------------------------------- // Common functions (CascCommon.cpp) inline void FreeCascBlob(PQUERY_KEY pBlob) { if(pBlob != NULL) { CASC_FREE(pBlob->pbData); pBlob->cbData = 0; } } //----------------------------------------------------------------------------- // Text file parsing (CascFiles.cpp) bool InvokeProgressCallback(TCascStorage * hs, LPCSTR szMessage, LPCSTR szObject, DWORD CurrentValue, DWORD TotalValue); DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PULONGLONG PtrEncodedSize = NULL); DWORD DownloadFileFromCDN(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo); DWORD CheckGameDirectory(TCascStorage * hs, LPTSTR szDirectory); DWORD LoadCdnsFile(TCascStorage * hs); DWORD LoadBuildInfo(TCascStorage * hs); DWORD LoadCdnConfigFile(TCascStorage * hs); DWORD LoadCdnBuildFile(TCascStorage * hs); LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData); LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData); bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle); bool SetCacheStrategy(HANDLE hFile, CSTRTG CacheStrategy); //----------------------------------------------------------------------------- // Internal file functions void * ProbeOutputBuffer(void * pvBuffer, size_t cbLength, size_t cbMinLength, size_t * pcbLengthNeeded); PCASC_CKEY_ENTRY FindCKeyEntry_CKey(TCascStorage * hs, LPBYTE pbCKey, PDWORD PtrIndex = NULL); PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD PtrIndex = NULL); size_t GetTagBitmapLength(LPBYTE pbFilePtr, LPBYTE pbFileEnd, DWORD EntryCount); DWORD CascDecompress(LPBYTE pvOutBuffer, PDWORD pcbOutBuffer, LPBYTE pvInBuffer, DWORD cbInBuffer); DWORD CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer); DWORD CascLoadEncryptionKeys(TCascStorage * hs); DWORD CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex); //----------------------------------------------------------------------------- // Support for index files bool CopyEKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry); DWORD LoadIndexFiles(TCascStorage * hs); void FreeIndexFiles(TCascStorage * hs); //----------------------------------------------------------------------------- // Support for ROOT file DWORD RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); DWORD RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); DWORD RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); DWORD RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask); DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); DWORD RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); //----------------------------------------------------------------------------- // Dumpers (CascDumpData.cpp) #ifdef _DEBUG void CascDumpFile(HANDLE hFile, const char * szDumpFile = NULL); void CascDumpStorage(HANDLE hStorage, const char * szDumpFile = NULL); #endif #endif // __CASCCOMMON_H__ ================================================ FILE: deps/CascLib/src/CascDecompress.cpp ================================================ /*****************************************************************************/ /* CascDecompress.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Decompression functions */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 02.05.14 1.00 Lad The first version of CascDecompress.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Public functions DWORD CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer) { z_stream z; // Stream information for zlib DWORD dwErrCode = ERROR_FILE_CORRUPT; uInt cbOutBuffer = *pcbOutBuffer; int nResult; // Fill the stream structure for zlib z.next_in = pbInBuffer; z.avail_in = cbInBuffer; z.total_in = cbInBuffer; z.next_out = pbOutBuffer; z.avail_out = cbOutBuffer; z.total_out = 0; z.zalloc = NULL; z.zfree = NULL; // Reset the total number of output bytes cbOutBuffer = 0; // Initialize the decompression structure if((nResult = inflateInit(&z)) == Z_OK) { // Call zlib to decompress the data nResult = inflate(&z, Z_NO_FLUSH); if (nResult == Z_OK || nResult == Z_STREAM_END) { // Give the size of the uncompressed data cbOutBuffer = z.total_out; dwErrCode = ERROR_SUCCESS; } inflateEnd(&z); } // Give the caller the number of bytes needed pcbOutBuffer[0] = cbOutBuffer; return dwErrCode; } ================================================ FILE: deps/CascLib/src/CascDecrypt.cpp ================================================ /*****************************************************************************/ /* CascDecrypt.cpp Copyright (c) Ladislav Zezula 2015 */ /*---------------------------------------------------------------------------*/ /* Decryption functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 31.10.15 1.00 Lad The first version of CascDecrypt.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Local structures // For Salsa20 decryption process typedef struct _CASC_SALSA20 { DWORD Key[CASC_KEY_LENGTH]; DWORD dwRounds; } CASC_SALSA20, *PCASC_SALSA20; // For static-stored keys struct CASC_ENCRYPTION_KEY { ULONGLONG KeyName; // "Name" of the key BYTE Key[CASC_KEY_LENGTH]; // The key itself }; // For keys inside the key map struct CASC_ENCRYPTION_KEY2 : public CASC_ENCRYPTION_KEY { CASC_ENCRYPTION_KEY2 * pNext; // Pointer to the next key wihh the same hash }; typedef CASC_ENCRYPTION_KEY2 * PCASC_ENCRYPTION_KEY2; //----------------------------------------------------------------------------- // Known encryption keys. See https://wowdev.wiki/CASC for updates static const char * szKeyConstant16 = "expand 16-byte k"; static const char * szKeyConstant32 = "expand 32-byte k"; static CASC_ENCRYPTION_KEY StaticCascKeys[] = { // Key Name Encryption key Seen in // ---------------------- --------------------------------------------------------------------------------------------------- ----------- // Battle.net app { 0x2C547F26A2613E01ULL, { 0x37, 0xC5, 0x0C, 0x10, 0x2D, 0x4C, 0x9E, 0x3A, 0x5A, 0xC0, 0x69, 0xF0, 0x72, 0xB1, 0x41, 0x7D } }, // Battle.net App Alpha 1.5.0 // Starcraft // { 0xD0CAE11366CEEA83ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 1.12.3.2609 (build 45364) // Warcraft III Reforged beta (build) { 0x6E4296823E7D561EULL, { 0xC0, 0xBF, 0xA2, 0x94, 0x3A, 0xC3, 0xE9, 0x22, 0x86, 0xE4, 0x44, 0x3E, 0xE3, 0x56, 0x0D, 0x65 } }, // 1.32.0.13369 Base content (Beta Week 0) { 0xE04D60E31DDEBF63ULL, { 0x26, 0x3D, 0xB5, 0xC4, 0x02, 0xDA, 0x8D, 0x4D, 0x68, 0x63, 0x09, 0xCB, 0x2E, 0x32, 0x54, 0xD0 } }, // 1.32.0.13445 Base content (Beta Week 1) // Overwatch { 0xFB680CB6A8BF81F3ULL, { 0x62, 0xD9, 0x0E, 0xFA, 0x7F, 0x36, 0xD7, 0x1C, 0x39, 0x8A, 0xE2, 0xF1, 0xFE, 0x37, 0xBD, 0xB9 } }, // 0.8.0.24919_retailx64 (hardcoded) { 0x402CD9D8D6BFED98ULL, { 0xAE, 0xB0, 0xEA, 0xDE, 0xA4, 0x76, 0x12, 0xFE, 0x6C, 0x04, 0x1A, 0x03, 0x95, 0x8D, 0xF2, 0x41 } }, // 0.8.0.24919_retailx64 (hardcoded) { 0xDBD3371554F60306ULL, { 0x34, 0xE3, 0x97, 0xAC, 0xE6, 0xDD, 0x30, 0xEE, 0xFD, 0xC9, 0x8A, 0x2A, 0xB0, 0x93, 0xCD, 0x3C } }, // 0.8.0.24919_retailx64 (streamed from server) { 0x11A9203C9881710AULL, { 0x2E, 0x2C, 0xB8, 0xC3, 0x97, 0xC2, 0xF2, 0x4E, 0xD0, 0xB5, 0xE4, 0x52, 0xF1, 0x8D, 0xC2, 0x67 } }, // 0.8.0.24919_retailx64 (streamed from server) { 0xA19C4F859F6EFA54ULL, { 0x01, 0x96, 0xCB, 0x6F, 0x5E, 0xCB, 0xAD, 0x7C, 0xB5, 0x28, 0x38, 0x91, 0xB9, 0x71, 0x2B, 0x4B } }, // 0.8.0.24919_retailx64 (streamed from server) { 0x87AEBBC9C4E6B601ULL, { 0x68, 0x5E, 0x86, 0xC6, 0x06, 0x3D, 0xFD, 0xA6, 0xC9, 0xE8, 0x52, 0x98, 0x07, 0x6B, 0x3D, 0x42 } }, // 0.8.0.24919_retailx64 (streamed from server) { 0xDEE3A0521EFF6F03ULL, { 0xAD, 0x74, 0x0C, 0xE3, 0xFF, 0xFF, 0x92, 0x31, 0x46, 0x81, 0x26, 0x98, 0x57, 0x08, 0xE1, 0xB9 } }, // 0.8.0.24919_retailx64 (streamed from server) { 0x8C9106108AA84F07ULL, { 0x53, 0xD8, 0x59, 0xDD, 0xA2, 0x63, 0x5A, 0x38, 0xDC, 0x32, 0xE7, 0x2B, 0x11, 0xB3, 0x2F, 0x29 } }, // 0.8.0.24919_retailx64 (streamed from server) { 0x49166D358A34D815ULL, { 0x66, 0x78, 0x68, 0xCD, 0x94, 0xEA, 0x01, 0x35, 0xB9, 0xB1, 0x6C, 0x93, 0xB1, 0x12, 0x4A, 0xBA } }, // 0.8.0.24919_retailx64 (streamed from server) { 0x1463A87356778D14ULL, { 0x69, 0xBD, 0x2A, 0x78, 0xD0, 0x5C, 0x50, 0x3E, 0x93, 0x99, 0x49, 0x59, 0xB3, 0x0E, 0x5A, 0xEC } }, // ? 1.0.3.0 (streamed from server) { 0x5E152DE44DFBEE01ULL, { 0xE4, 0x5A, 0x17, 0x93, 0xB3, 0x7E, 0xE3, 0x1A, 0x8E, 0xB8, 0x5C, 0xEE, 0x0E, 0xEE, 0x1B, 0x68 } }, // ? 1.0.3.0 (streamed from server) { 0x9B1F39EE592CA415ULL, { 0x54, 0xA9, 0x9F, 0x08, 0x1C, 0xAD, 0x0D, 0x08, 0xF7, 0xE3, 0x36, 0xF4, 0x36, 0x8E, 0x89, 0x4C } }, // ? 1.0.3.0 (streamed from server) { 0x24C8B75890AD5917ULL, { 0x31, 0x10, 0x0C, 0x00, 0xFD, 0xE0, 0xCE, 0x18, 0xBB, 0xB3, 0x3F, 0x3A, 0xC1, 0x5B, 0x30, 0x9F } }, // ? 1.0.3.0 (included in game) { 0xEA658B75FDD4890FULL, { 0xDE, 0xC7, 0xA4, 0xE7, 0x21, 0xF4, 0x25, 0xD1, 0x33, 0x03, 0x98, 0x95, 0xC3, 0x60, 0x36, 0xF8 } }, // ? 1.0.3.0 (included in game) { 0x026FDCDF8C5C7105ULL, { 0x8F, 0x41, 0x80, 0x9D, 0xA5, 0x53, 0x66, 0xAD, 0x41, 0x6D, 0x3C, 0x33, 0x74, 0x59, 0xEE, 0xE3 } }, // (included in game) { 0xCAE3FAC925F20402ULL, { 0x98, 0xB7, 0x8E, 0x87, 0x74, 0xBF, 0x27, 0x50, 0x93, 0xCB, 0x1B, 0x5F, 0xC7, 0x14, 0x51, 0x1B } }, // (included in game) { 0x061581CA8496C80CULL, { 0xDA, 0x2E, 0xF5, 0x05, 0x2D, 0xB9, 0x17, 0x38, 0x0B, 0x8A, 0xA6, 0xEF, 0x7A, 0x5F, 0x8E, 0x6A } }, // { 0xBE2CB0FAD3698123ULL, { 0x90, 0x2A, 0x12, 0x85, 0x83, 0x6C, 0xE6, 0xDA, 0x58, 0x95, 0x02, 0x0D, 0xD6, 0x03, 0xB0, 0x65 } }, // { 0x57A5A33B226B8E0AULL, { 0xFD, 0xFC, 0x35, 0xC9, 0x9B, 0x9D, 0xB1, 0x1A, 0x32, 0x62, 0x60, 0xCA, 0x24, 0x6A, 0xCB, 0x41 } }, // 1.1.0.0.30200 Ana { 0x42B9AB1AF5015920ULL, { 0xC6, 0x87, 0x78, 0x82, 0x3C, 0x96, 0x4C, 0x6F, 0x24, 0x7A, 0xCC, 0x0F, 0x4A, 0x25, 0x84, 0xF8 } }, // 1.2.0.1.30684 Summer Games { 0x4F0FE18E9FA1AC1AULL, { 0x89, 0x38, 0x1C, 0x74, 0x8F, 0x65, 0x31, 0xBB, 0xFC, 0xD9, 0x77, 0x53, 0xD0, 0x6C, 0xC3, 0xCD } }, // 1.2.0.1.30684 { 0x7758B2CF1E4E3E1BULL, { 0x3D, 0xE6, 0x0D, 0x37, 0xC6, 0x64, 0x72, 0x35, 0x95, 0xF2, 0x7C, 0x5C, 0xDB, 0xF0, 0x8B, 0xFA } }, // 1.2.0.1.30684 { 0xE5317801B3561125ULL, { 0x7D, 0xD0, 0x51, 0x19, 0x9F, 0x84, 0x01, 0xF9, 0x5E, 0x4C, 0x03, 0xC8, 0x84, 0xDC, 0xEA, 0x33 } }, // 1.4.0.2.32143 Halloween Terror { 0x16B866D7BA3A8036ULL, { 0x13, 0x95, 0xE8, 0x82, 0xBF, 0x25, 0xB4, 0x81, 0xF6, 0x1A, 0x4D, 0x62, 0x11, 0x41, 0xDA, 0x6E } }, // 1.4.1.0.31804 Bastion Blizzcon 2016 skin { 0x11131FFDA0D18D30ULL, { 0xC3, 0x2A, 0xD1, 0xB8, 0x25, 0x28, 0xE0, 0xA4, 0x56, 0x89, 0x7B, 0x3C, 0xE1, 0xC2, 0xD2, 0x7E } }, // 1.5.0.1.32795 Sombra { 0xCAC6B95B2724144AULL, { 0x73, 0xE4, 0xBE, 0xA1, 0x45, 0xDF, 0x2B, 0x89, 0xB6, 0x5A, 0xEF, 0x02, 0xF8, 0x3F, 0xA2, 0x60 } }, // 1.5.0.1.32795 Ecopoint: Antarctica { 0xB7DBC693758A5C36ULL, { 0xBC, 0x3A, 0x92, 0xBF, 0xE3, 0x02, 0x51, 0x8D, 0x91, 0xCC, 0x30, 0x79, 0x06, 0x71, 0xBF, 0x10 } }, // 1.5.0.1.32795 Genji Oni skin { 0x90CA73B2CDE3164BULL, { 0x5C, 0xBF, 0xF1, 0x1F, 0x22, 0x72, 0x0B, 0xAC, 0xC2, 0xAE, 0x6A, 0xAD, 0x8F, 0xE5, 0x33, 0x17 } }, // 1.6.1.0.33236 Oasis map { 0x6DD3212FB942714AULL, { 0xE0, 0x2C, 0x16, 0x43, 0x60, 0x2E, 0xC1, 0x6C, 0x3A, 0xE2, 0xA4, 0xD2, 0x54, 0xA0, 0x8F, 0xD9 } }, // 1.6.1.0.33236 { 0x11DDB470ABCBA130ULL, { 0x66, 0x19, 0x87, 0x66, 0xB1, 0xC4, 0xAF, 0x75, 0x89, 0xEF, 0xD1, 0x3A, 0xD4, 0xDD, 0x66, 0x7A } }, // 1.6.1.0.33236 Winter Wonderland { 0x5BEF27EEE95E0B4BULL, { 0x36, 0xBC, 0xD2, 0xB5, 0x51, 0xFF, 0x1C, 0x84, 0xAA, 0x3A, 0x39, 0x94, 0xCC, 0xEB, 0x03, 0x3E } }, // { 0x9359B46E49D2DA42ULL, { 0x17, 0x3D, 0x65, 0xE7, 0xFC, 0xAE, 0x29, 0x8A, 0x93, 0x63, 0xBD, 0x6A, 0xA1, 0x89, 0xF2, 0x00 } }, // Diablo's 20th anniversary { 0x1A46302EF8896F34ULL, { 0x80, 0x29, 0xAD, 0x54, 0x51, 0xD4, 0xBC, 0x18, 0xE9, 0xD0, 0xF5, 0xAC, 0x44, 0x9D, 0xC0, 0x55 } }, // 1.7.0.2.34156 Year of the Rooster { 0x693529F7D40A064CULL, { 0xCE, 0x54, 0x87, 0x3C, 0x62, 0xDA, 0xA4, 0x8E, 0xFF, 0x27, 0xFC, 0xC0, 0x32, 0xBD, 0x07, 0xE3 } }, // 1.8.0.0.34470 CTF Maps { 0x388B85AEEDCB685DULL, { 0xD9, 0x26, 0xE6, 0x59, 0xD0, 0x4A, 0x09, 0x6B, 0x24, 0xC1, 0x91, 0x51, 0x07, 0x6D, 0x37, 0x9A } }, // 1.8.0.0.34470 Numbani Update (Doomfist teaser) { 0xE218F69AAC6C104DULL, { 0xF4, 0x3D, 0x12, 0xC9, 0x4A, 0x9A, 0x52, 0x84, 0x97, 0x97, 0x1F, 0x1C, 0xBE, 0x41, 0xAD, 0x4D } }, // 1.9.0.0.34986 Orisa { 0xF432F0425363F250ULL, { 0xBA, 0x69, 0xF2, 0xB3, 0x3C, 0x27, 0x68, 0xF5, 0xF2, 0x9B, 0xFE, 0x78, 0xA5, 0xA1, 0xFA, 0xD5 } }, // 1.10.0.0.35455 Uprising { 0x061D52F86830B35DULL, { 0xD7, 0x79, 0xF9, 0xC6, 0xCC, 0x9A, 0x4B, 0xE1, 0x03, 0xA4, 0xE9, 0x0A, 0x73, 0x38, 0xF7, 0x93 } }, // 1.10.0.0.35455 D.Va Officer Skin (HotS Nexus Challenge 2) { 0x1275C84CF113EF65ULL, { 0xCF, 0x58, 0xB6, 0x93, 0x3E, 0xAF, 0x98, 0xAF, 0x53, 0xE7, 0x6F, 0x84, 0x26, 0xCC, 0x7E, 0x6C } }, // { 0xD9C7C7AC0F14C868ULL, { 0x3A, 0xFD, 0xF6, 0x8E, 0x3A, 0x5D, 0x63, 0xBA, 0xBA, 0x1E, 0x68, 0x21, 0x88, 0x3F, 0x06, 0x7D } }, // { 0xBD4E42661A432951ULL, { 0x6D, 0xE8, 0xE2, 0x8C, 0x85, 0x11, 0x64, 0x4D, 0x55, 0x95, 0xFC, 0x45, 0xE5, 0x35, 0x14, 0x72 } }, // 1.11.0.0.36376 Anniversary event { 0xC43CB14355249451ULL, { 0x0E, 0xA2, 0xB4, 0x4F, 0x96, 0xA2, 0x69, 0xA3, 0x86, 0x85, 0x6D, 0x04, 0x9A, 0x3D, 0xEC, 0x86 } }, // 1.12.0.0.37104 Horizon Lunar Colony { 0xE6D914F8E4744953ULL, { 0xC8, 0x47, 0x7C, 0x28, 0x9D, 0xCE, 0x66, 0xD9, 0x13, 0x65, 0x07, 0xA3, 0x3A, 0xA3, 0x33, 0x01 } }, // 1.13.0.0.37646 Doomfist { 0x5694C503F8C80178ULL, { 0x7F, 0x4C, 0xF1, 0xC1, 0xFB, 0xBA, 0xD9, 0x2B, 0x18, 0x43, 0x36, 0xD6, 0x77, 0xEB, 0xF9, 0x37 } }, // 1.13.0.0.37646 Doomfist { 0x21DBFD65F3E54269ULL, { 0xAB, 0x58, 0x0C, 0x38, 0x37, 0xCA, 0xF8, 0xA4, 0x61, 0xF2, 0x43, 0xA5, 0x66, 0xB2, 0xAE, 0x4D } }, // 1.13.0.0.37646 Summer Games 2017 // { 0x27ABA5F88DD8D078ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 1.13.0.0.37646 { 0x21E1F90E71D33C71ULL, { 0x32, 0x87, 0x42, 0x33, 0x91, 0x62, 0xB3, 0x26, 0x76, 0xC8, 0x03, 0xC2, 0x25, 0x59, 0x31, 0xA6 } }, // 1.14.1.0.39083 Deathmatch { 0xD9CB055BCDD40B6EULL, { 0x49, 0xFB, 0x44, 0x77, 0xA4, 0xA0, 0x82, 0x53, 0x27, 0xE9, 0xA7, 0x36, 0x82, 0xBE, 0xCD, 0x0C } }, // 1.15.0.0.????? Junkertown { 0x8175CE3C694C6659ULL, { 0xE3, 0xF3, 0xFA, 0x77, 0x26, 0xC7, 0x0D, 0x26, 0xAE, 0x13, 0x0D, 0x96, 0x9D, 0xDD, 0xF3, 0x99 } }, // 1.16.0.0.40011 Halloween 2017 { 0xB8DE51690075435AULL, { 0xC0, 0x7E, 0x92, 0x60, 0xBB, 0x71, 0x12, 0x17, 0xE7, 0xDE, 0x6F, 0xED, 0x91, 0x1F, 0x42, 0x96 } }, // 1.16.0.0.????? Winston Blizzcon 2017 skin { 0xF6CF23955B5D437DULL, { 0xAE, 0xBA, 0x22, 0x73, 0x28, 0xA5, 0xB0, 0xAA, 0x9F, 0x51, 0xDA, 0xE3, 0xF6, 0xA7, 0xDF, 0xE4 } }, // 1.17.0.2.41350 Moira { 0x0E4D9426F2891F5CULL, { 0x9F, 0xF0, 0x64, 0xC3, 0x8B, 0xE5, 0x2C, 0xCD, 0xF7, 0x37, 0x48, 0x18, 0x0F, 0x62, 0x82, 0x05 } }, // 1.18.1.2.42076 Winter Wonderland 2017 { 0x9240BA6A2A0CF684ULL, { 0xDF, 0x2E, 0x37, 0xD7, 0x8B, 0x43, 0x10, 0x8F, 0xA6, 0x24, 0x20, 0x68, 0xB7, 0x0D, 0x1F, 0x65 } }, // 1.19.1.3.42563 Overwatch League { 0x82297FBAB7F5EB80ULL, { 0xB5, 0x34, 0xC2, 0x09, 0x65, 0x85, 0x2F, 0xB1, 0x5A, 0xEC, 0xAC, 0x17, 0xE3, 0x81, 0xB4, 0x17 } }, // 1.19.1.3.42563 Jan 2017 Lootbox Update { 0x9ADF00AA1A174A69ULL, { 0x9A, 0x4A, 0xC8, 0x99, 0x26, 0x1A, 0x2F, 0x1C, 0x69, 0x69, 0xF3, 0x93, 0x97, 0xC3, 0x58, 0xE7 } }, // 1.19.1.3.42563 Blizzard World { 0xCFA05AA76B49F881ULL, { 0x52, 0x6D, 0xDD, 0xEF, 0x19, 0xBF, 0x37, 0x3C, 0x25, 0xB6, 0x29, 0xA3, 0x34, 0xCD, 0x72, 0x37 } }, // 1.19.1.3.42563 WoW's Battle For Azeroth Preorder { 0x493455579DA0B18EULL, { 0xC0, 0xBA, 0xBF, 0x72, 0xAD, 0x2C, 0x05, 0xDF, 0xC1, 0x40, 0x17, 0xD1, 0xAD, 0xBF, 0x59, 0x77 } }, // 1.19.3.1.43036 Inaugural Season Spray/Icon ?? { 0x6362C5AD65DAE686ULL, { 0x62, 0xF6, 0x03, 0xD5, 0x39, 0x0F, 0x76, 0x3E, 0xD5, 0x17, 0x73, 0xF0, 0x16, 0x4F, 0xED, 0xB5 } }, // 1.19.3.1.43036 White/Gray OWL Skins ?? { 0x8162E5313A9C135DULL, { 0xF4, 0x07, 0x83, 0x4D, 0x95, 0x21, 0x58, 0x7C, 0x50, 0x12, 0xB0, 0xA5, 0x9D, 0x7E, 0x06, 0x4B } }, // 1.20.0.2.43435 Lunar New Year 2018 (Dog) / Ayutthaya / Comp CTF // { 0x68EAE8FDC008C381ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 1.20.0.2.43435 { 0xF412C6327C4BF091ULL, { 0x6F, 0xAF, 0xC6, 0x48, 0xCB, 0xF1, 0xC2, 0x11, 0x5B, 0x76, 0x95, 0x93, 0xC1, 0x70, 0xE7, 0x32 } }, // 1.20.0.2.43435 SC2 20th Anniversary (Kerrigan Skin) { 0x3B3ED0874091B174ULL, { 0x5D, 0x09, 0xC2, 0x68, 0x8B, 0x1D, 0x9F, 0x1A, 0x4D, 0xB6, 0x46, 0x02, 0xC1, 0x66, 0x1D, 0x24 } }, // 1.21.?.?.????? Brigitte { 0x37FD04E05D2A6292ULL, { 0xF0, 0x64, 0x55, 0xE5, 0x6C, 0xD1, 0x44, 0x91, 0x42, 0x95, 0xF2, 0xEF, 0x15, 0x3D, 0x23, 0xBA } }, // 1.21.?.?.????? Brigitte Cosmetics { 0xC0DDC77552BE5794ULL, { 0xF2, 0xBB, 0x7F, 0x35, 0x99, 0x0E, 0x29, 0x00, 0xCB, 0xD8, 0x77, 0xB4, 0xD3, 0xA7, 0x13, 0x9C } }, // 1.22.?.?.????? OWL away skins { 0x68D8EB839DC15D75ULL, { 0x29, 0xAF, 0xFE, 0xCA, 0x52, 0x99, 0xC4, 0x14, 0x0A, 0x12, 0xA6, 0x6F, 0x95, 0x4E, 0xF1, 0xE3 } }, // 1.22.?.?.????? Archives 2018 (Retribution) { 0x209F33BBAC9D1295ULL, { 0xBD, 0x53, 0x54, 0x38, 0xD0, 0xCD, 0xEE, 0x0E, 0x95, 0x67, 0xE0, 0xEF, 0x67, 0x1C, 0x80, 0x9F } }, // 1.23.?.?.????? Rialto { 0xA55F8C6F20454D94ULL, { 0x42, 0xD7, 0x62, 0x85, 0x41, 0x24, 0x61, 0xB0, 0xC7, 0x5A, 0xB7, 0x5F, 0xB5, 0x2F, 0x59, 0x6E } }, // 1.23.?.?.????? Mercy BCRF Items { 0x3EEEDB8E7C29A09BULL, { 0x83, 0x7A, 0xC7, 0x93, 0x05, 0xE4, 0xBF, 0xBA, 0x3B, 0x22, 0x26, 0xF1, 0xB9, 0x82, 0x00, 0xBF } }, // 1.24.?.?.????? Anniversary 2018 Map/Items { 0x22C1AF6758F8449EULL, { 0x28, 0xAA, 0x1B, 0xD2, 0xB9, 0xA1, 0xE3, 0x63, 0x39, 0x89, 0xB1, 0xBB, 0xF6, 0x4A, 0xEA, 0xC4 } }, // 1.26.?.?.????? Emily / Comp Season 11 / Comp 3v3 Items { 0x11B2E01B9331799EULL, { 0x9A, 0x1C, 0x99, 0x30, 0x3D, 0x6A, 0x58, 0x97, 0x8E, 0x29, 0xC9, 0x88, 0x73, 0x32, 0x7A, 0x6A } }, // 1.26.?.?.????? Wrecking Ball { 0x8498337C740329B3ULL, { 0xEC, 0x73, 0xD6, 0x63, 0xE3, 0xFC, 0x72, 0x41, 0x6B, 0x3E, 0x46, 0x79, 0x15, 0x70, 0x8B, 0x4F } }, // 1.26.?.?.????? Wrecking Ball Cosmetics { 0xE6E8BCABE3CC96C1ULL, { 0x57, 0x72, 0x7E, 0x52, 0x60, 0x06, 0x65, 0xEA, 0xC0, 0x2B, 0xD8, 0x7F, 0x06, 0x92, 0x89, 0x8F } }, // 1.26.?.?.????? OWL Grand Finals Unlocks / Lucio Emote { 0x5D4AC0DC6F3113BAULL, { 0xD1, 0xC9, 0xF1, 0xFF, 0x69, 0x58, 0x5D, 0x13, 0x98, 0xEB, 0xA0, 0x46, 0x34, 0x81, 0xF5, 0xCF } }, // 1.27.?.?.????? Summer Games 2018 { 0x27F9D85973DCD5AFULL, { 0xC5, 0xFE, 0x10, 0x15, 0xBC, 0xE0, 0xB7, 0x84, 0x8F, 0x02, 0x28, 0x68, 0xAF, 0xF6, 0x54, 0xD1 } }, // 1.27.?.?.????? Summer Games 2018 { 0x67AAD845CC0F03BDULL, { 0x6C, 0xD8, 0xAD, 0x3F, 0x37, 0xF5, 0x4A, 0xBE, 0xC7, 0x63, 0x02, 0x94, 0xD4, 0x90, 0x41, 0xBF } }, // 1.27.?.?.????? D.Va Nano Cola Challenge { 0xCA13F0C79042A1A0ULL, { 0xFC, 0xD8, 0x9C, 0xE8, 0x81, 0x2E, 0x63, 0x46, 0x07, 0x6F, 0xC8, 0x2D, 0xD7, 0xA9, 0x24, 0x87 } }, // 1.28.?.?.????? Busan { 0xC4D84093A32684BDULL, { 0x38, 0xE1, 0x82, 0x42, 0x3E, 0xED, 0xD8, 0xE3, 0xF5, 0x7A, 0xC1, 0xD4, 0x07, 0xB4, 0x70, 0xD0 } }, // 1.28.?.?.????? Blizzcon 2018 Sombra Demon Hunter Skin { 0x0BFE5A2B3C606BA1ULL, { 0xD6, 0x41, 0x9B, 0x8E, 0x42, 0x82, 0x0B, 0x0B, 0x24, 0xE0, 0x8D, 0xA4, 0x44, 0xA0, 0x68, 0x22 } }, // 1.29.?.?.????? Halloween Terror 2018 { 0x402CD9D8D6BFED98ULL, { 0xAE, 0xB0, 0xEA, 0xDE, 0xA4, 0x76, 0x12, 0xFE, 0x6C, 0x04, 0x1A, 0x03, 0x95, 0x8D, 0xF2, 0x41 } }, // 1.29.?.?.????? { 0xF1CBDF48147D26C6ULL, { 0x4B, 0x69, 0x44, 0x69, 0x51, 0x57, 0xD4, 0x3D, 0x33, 0xE4, 0x0B, 0x56, 0x92, 0x44, 0x5A, 0xDB } }, // 1.30.?.?.????? 1.30 + World Cup Viewer { 0x01A48CFAE25F85CDULL, { 0x60, 0xD7, 0x7A, 0x58, 0x06, 0x2D, 0x03, 0xDC, 0x69, 0x3C, 0x01, 0x95, 0x4D, 0xD1, 0x80, 0x21 } }, // 1.30.?.?.????? 1.30 + World Cup Viewer { 0xFEBBF66DAEF6C9BEULL, { 0x4A, 0x22, 0x0A, 0xE3, 0xA6, 0x80, 0x8E, 0xD2, 0x69, 0x74, 0x10, 0xC9, 0x4C, 0x1C, 0xE9, 0x70 } }, // 1.30.0.1.????? Ashe { 0x2AD4834DAB3986ABULL, { 0xEA, 0x69, 0x71, 0xF7, 0x8A, 0xBE, 0xBD, 0x2A, 0xF8, 0x83, 0xD4, 0x6A, 0x54, 0x86, 0xF3, 0x9F } }, // 1.31.?.?.????? Winter 2018 { 0xD89B24D62F00A04EULL, { 0xC3, 0xA4, 0xD0, 0x10, 0xAA, 0xA7, 0x28, 0x7A, 0x3E, 0xAC, 0xCD, 0x45, 0xED, 0x47, 0x13, 0x45 } }, // 1.31.?.?.????? Bastet Challenge { 0x32DDC40236DAEA7BULL, { 0x2D, 0xBD, 0xE4, 0xFB, 0x9F, 0xDA, 0x77, 0x6A, 0xA2, 0x94, 0x87, 0x08, 0x54, 0xFE, 0x9B, 0x02 } }, // 1.31.?.?.????? Lunar New Year 2019 (Pig) { 0xF481EFC2302EE415ULL, { 0x37, 0xA7, 0xF2, 0xB8, 0x7D, 0x0B, 0x8B, 0x70, 0x04, 0x56, 0xC6, 0xA9, 0x2C, 0x71, 0xD6, 0xDD } }, // 1.33.?.?.????? Paris Map { 0xD1AC8C1903524D9AULL, { 0xD7, 0x81, 0xD0, 0xAA, 0x35, 0xE5, 0xC1, 0x06, 0xBC, 0xA7, 0xCF, 0x01, 0xDE, 0xBD, 0x14, 0x94 } }, // 1.34.0.1.55918 Baptiste { 0x71EEBE93590AA903ULL, { 0x0C, 0xCD, 0x10, 0xD4, 0x55, 0x3E, 0xEC, 0x7E, 0x97, 0xFD, 0x36, 0xA9, 0xE8, 0xAD, 0xF0, 0xFF } }, // 1.34.0.1.55918 Baptiste Cosmetics // Streamed WoW keys { 0xFA505078126ACB3EULL, { 0xBD, 0xC5, 0x18, 0x62, 0xAB, 0xED, 0x79, 0xB2, 0xDE, 0x48, 0xC8, 0xE7, 0xE6, 0x6C, 0x62, 0x00 } }, // 15 WOW-20740patch7.0.1_Beta { 0xFF813F7D062AC0BCULL, { 0xAA, 0x0B, 0x5C, 0x77, 0xF0, 0x88, 0xCC, 0xC2, 0xD3, 0x90, 0x49, 0xBD, 0x26, 0x7F, 0x06, 0x6D } }, // 25 WOW-20740patch7.0.1_Beta { 0xD1E9B5EDF9283668ULL, { 0x8E, 0x4A, 0x25, 0x79, 0x89, 0x4E, 0x38, 0xB4, 0xAB, 0x90, 0x58, 0xBA, 0x5C, 0x73, 0x28, 0xEE } }, // 39 WOW-20740patch7.0.1_Beta Enchanted Torch pet { 0xB76729641141CB34ULL, { 0x98, 0x49, 0xD1, 0xAA, 0x7B, 0x1F, 0xD0, 0x98, 0x19, 0xC5, 0xC6, 0x62, 0x83, 0xA3, 0x26, 0xEC } }, // 40 WOW-20740patch7.0.1_Beta Enchanted Pen pet { 0xFFB9469FF16E6BF8ULL, { 0xD5, 0x14, 0xBD, 0x19, 0x09, 0xA9, 0xE5, 0xDC, 0x87, 0x03, 0xF4, 0xB8, 0xBB, 0x1D, 0xFD, 0x9A } }, // 41 WOW-20740patch7.0.1_Beta { 0x23C5B5DF837A226CULL, { 0x14, 0x06, 0xE2, 0xD8, 0x73, 0xB6, 0xFC, 0x99, 0x21, 0x7A, 0x18, 0x08, 0x81, 0xDA, 0x8D, 0x62 } }, // 42 WOW-20740patch7.0.1_Beta Enchanted Cauldron pet // { 0x3AE403EF40AC3037ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 51 WOW-21249patch7.0.3_Beta { 0xE2854509C471C554ULL, { 0x43, 0x32, 0x65, 0xF0, 0xCD, 0xEB, 0x2F, 0x4E, 0x65, 0xC0, 0xEE, 0x70, 0x08, 0x71, 0x4D, 0x9E } }, // 52 WOW-21249patch7.0.3_Beta Warcraft movie items { 0x8EE2CB82178C995AULL, { 0xDA, 0x6A, 0xFC, 0x98, 0x9E, 0xD6, 0xCA, 0xD2, 0x79, 0x88, 0x59, 0x92, 0xC0, 0x37, 0xA8, 0xEE } }, // 55 WOW-21531patch7.0.3_Beta BlizzCon 2016 Murlocs { 0x5813810F4EC9B005ULL, { 0x01, 0xBE, 0x8B, 0x43, 0x14, 0x2D, 0xD9, 0x9A, 0x9E, 0x69, 0x0F, 0xAD, 0x28, 0x8B, 0x60, 0x82 } }, // 56 WOW-21531patch7.0.3_Beta Fel Kitten { 0x7F9E217166ED43EAULL, { 0x05, 0xFC, 0x92, 0x7B, 0x9F, 0x4F, 0x5B, 0x05, 0x56, 0x81, 0x42, 0x91, 0x2A, 0x05, 0x2B, 0x0F } }, // 57 WOW-21531patch7.0.3_Beta Legion music { 0xC4A8D364D23793F7ULL, { 0xD1, 0xAC, 0x20, 0xFD, 0x14, 0x95, 0x7F, 0xAB, 0xC2, 0x71, 0x96, 0xE9, 0xF6, 0xE7, 0x02, 0x4A } }, // 58 WOW-21691patch7.0.3_Beta Demon Hunter #1 cinematic (legion_dh1) { 0x40A234AEBCF2C6E5ULL, { 0xC6, 0xC5, 0xF6, 0xC7, 0xF7, 0x35, 0xD7, 0xD9, 0x4C, 0x87, 0x26, 0x7F, 0xA4, 0x99, 0x4D, 0x45 } }, // 59 WOW-21691patch7.0.3_Beta Demon Hunter #2 cinematic (legion_dh2) { 0x9CF7DFCFCBCE4AE5ULL, { 0x72, 0xA9, 0x7A, 0x24, 0xA9, 0x98, 0xE3, 0xA5, 0x50, 0x0F, 0x38, 0x71, 0xF3, 0x76, 0x28, 0xC0 } }, // 60 WOW-21691patch7.0.3_Beta Val'sharah #1 cinematic (legion_val_yd) { 0x4E4BDECAB8485B4FULL, { 0x38, 0x32, 0xD7, 0xC4, 0x2A, 0xAC, 0x92, 0x68, 0xF0, 0x0B, 0xE7, 0xB6, 0xB4, 0x8E, 0xC9, 0xAF } }, // 61 WOW-21691patch7.0.3_Beta Val'sharah #2 cinematic (legion_val_yx) { 0x94A50AC54EFF70E4ULL, { 0xC2, 0x50, 0x1A, 0x72, 0x65, 0x4B, 0x96, 0xF8, 0x63, 0x50, 0xC5, 0xA9, 0x27, 0x96, 0x2F, 0x7A } }, // 62 WOW-21691patch7.0.3_Beta Sylvanas warchief cinematic (legion_org_vs) { 0xBA973B0E01DE1C2CULL, { 0xD8, 0x3B, 0xBC, 0xB4, 0x6C, 0xC4, 0x38, 0xB1, 0x7A, 0x48, 0xE7, 0x6C, 0x4F, 0x56, 0x54, 0xA3 } }, // 63 WOW-21691patch7.0.3_Beta Stormheim Sylvanas vs Greymane cinematic (legion_sth) { 0x494A6F8E8E108BEFULL, { 0xF0, 0xFD, 0xE1, 0xD2, 0x9B, 0x27, 0x4F, 0x6E, 0x7D, 0xBD, 0xB7, 0xFF, 0x81, 0x5F, 0xE9, 0x10 } }, // 64 WOW-21691patch7.0.3_Beta Harbingers Gul'dan video (legion_hrb_g) { 0x918D6DD0C3849002ULL, { 0x85, 0x70, 0x90, 0xD9, 0x26, 0xBB, 0x28, 0xAE, 0xDA, 0x4B, 0xF0, 0x28, 0xCA, 0xCC, 0x4B, 0xA3 } }, // 65 WOW-21691patch7.0.3_Beta Harbingers Khadgar video (legion_hrb_k) { 0x0B5F6957915ADDCAULL, { 0x4D, 0xD0, 0xDC, 0x82, 0xB1, 0x01, 0xC8, 0x0A, 0xBA, 0xC0, 0xA4, 0xD5, 0x7E, 0x67, 0xF8, 0x59 } }, // 66 WOW-21691patch7.0.3_Beta Harbingers Illidan video (legion_hrb_i) { 0x794F25C6CD8AB62BULL, { 0x76, 0x58, 0x3B, 0xDA, 0xCD, 0x52, 0x57, 0xA3, 0xF7, 0x3D, 0x15, 0x98, 0xA2, 0xCA, 0x2D, 0x99 } }, // 67 WOW-21846patch7.0.3_Beta Suramar cinematic (legion_su_i) { 0xA9633A54C1673D21ULL, { 0x1F, 0x8D, 0x46, 0x7F, 0x5D, 0x6D, 0x41, 0x1F, 0x8A, 0x54, 0x8B, 0x63, 0x29, 0xA5, 0x08, 0x7E } }, // 68 WOW-21846patch7.0.3_Beta legion_su_r cinematic { 0x5E5D896B3E163DEAULL, { 0x8A, 0xCE, 0x8D, 0xB1, 0x69, 0xE2, 0xF9, 0x8A, 0xC3, 0x6A, 0xD5, 0x2C, 0x08, 0x8E, 0x77, 0xC1 } }, // 69 WOW-21846patch7.0.3_Beta Broken Shore intro cinematic (legion_bs_i) { 0x0EBE36B5010DFD7FULL, { 0x9A, 0x89, 0xCC, 0x7E, 0x3A, 0xCB, 0x29, 0xCF, 0x14, 0xC6, 0x0B, 0xC1, 0x3B, 0x1E, 0x46, 0x16 } }, // 70 WOW-21846patch7.0.3_Beta Alliance Broken Shore cinematic (legion_bs_a) { 0x01E828CFFA450C0FULL, { 0x97, 0x2B, 0x6E, 0x74, 0x42, 0x0E, 0xC5, 0x19, 0xE6, 0xF9, 0xD9, 0x7D, 0x59, 0x4A, 0xA3, 0x7C } }, // 71 WOW-21846patch7.0.3_Beta Horde Broken Shore cinematic (legion_bs_h) { 0x4A7BD170FE18E6AEULL, { 0xAB, 0x55, 0xAE, 0x1B, 0xF0, 0xC7, 0xC5, 0x19, 0xAF, 0xF0, 0x28, 0xC1, 0x56, 0x10, 0xA4, 0x5B } }, // 72 WOW-21846patch7.0.3_Beta Khadgar & Light's Heart cinematic (legion_iq_lv) { 0x69549CB975E87C4FULL, { 0x7B, 0x6F, 0xA3, 0x82, 0xE1, 0xFA, 0xD1, 0x46, 0x5C, 0x85, 0x1E, 0x3F, 0x47, 0x34, 0xA1, 0xB3 } }, // 73 WOW-21846patch7.0.3_Beta legion_iq_id cinematic { 0x460C92C372B2A166ULL, { 0x94, 0x6D, 0x56, 0x59, 0xF2, 0xFA, 0xF3, 0x27, 0xC0, 0xB7, 0xEC, 0x82, 0x8B, 0x74, 0x8A, 0xDB } }, // 74 WOW-21952patch7.0.3_Beta Stormheim Alliance cinematic (legion_g_a_sth) { 0x8165D801CCA11962ULL, { 0xCD, 0x0C, 0x0F, 0xFA, 0xAD, 0x93, 0x63, 0xEC, 0x14, 0xDD, 0x25, 0xEC, 0xDD, 0x2A, 0x5B, 0x62 } }, // 75 WOW-21952patch7.0.3_Beta Stormheim Horde cinematic (legion_g_h_sth) { 0xA3F1C999090ADAC9ULL, { 0xB7, 0x2F, 0xEF, 0x4A, 0x01, 0x48, 0x8A, 0x88, 0xFF, 0x02, 0x28, 0x0A, 0xA0, 0x7A, 0x92, 0xBB } }, // 81 WOW-22578patch7.1.0_PTR Firecat Mount // { 0x18AFDF5191923610ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 82 WOW-22578patch7.1.0_PTR // { 0x3C258426058FBD93ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 91 WOW-23436patch7.2.0_PTR { 0x094E9A0474876B98ULL, { 0xE5, 0x33, 0xBB, 0x6D, 0x65, 0x72, 0x7A, 0x58, 0x32, 0x68, 0x0D, 0x62, 0x0B, 0x0B, 0xC1, 0x0B } }, // 92 WOW-23910patch7.2.5_PTR shadowstalkerpanthermount, shadowstalkerpantherpet { 0x3DB25CB86A40335EULL, { 0x02, 0x99, 0x0B, 0x12, 0x26, 0x0C, 0x1E, 0x9F, 0xDD, 0x73, 0xFE, 0x47, 0xCB, 0xAB, 0x70, 0x24 } }, // 93 WOW-23789patch7.2.0_PTR legion_72_ots { 0x0DCD81945F4B4686ULL, { 0x1B, 0x78, 0x9B, 0x87, 0xFB, 0x3C, 0x92, 0x38, 0xD5, 0x28, 0x99, 0x7B, 0xFA, 0xB4, 0x41, 0x86 } }, // 94 WOW-23789patch7.2.0_PTR legion_72_tst { 0x486A2A3A2803BE89ULL, { 0x32, 0x67, 0x9E, 0xA7, 0xB0, 0xF9, 0x9E, 0xBF, 0x4F, 0xA1, 0x70, 0xE8, 0x47, 0xEA, 0x43, 0x9A } }, // 95 WOW-23789patch7.2.0_PTR legion_72_ars { 0x71F69446AD848E06ULL, { 0xE7, 0x9A, 0xEB, 0x88, 0xB1, 0x50, 0x9F, 0x62, 0x8F, 0x38, 0x20, 0x82, 0x01, 0x74, 0x1C, 0x30 } }, // 97 WOW-24473patch7.3.0_PTR BlizzCon 2017 Mounts (AllianceShipMount and HordeZeppelinMount) { 0x211FCD1265A928E9ULL, { 0xA7, 0x36, 0xFB, 0xF5, 0x8D, 0x58, 0x7B, 0x39, 0x72, 0xCE, 0x15, 0x4A, 0x86, 0xAE, 0x45, 0x40 } }, // 98 WOW-24473patch7.3.0_PTR "Shadow" fox pet (store) { 0x0ADC9E327E42E98CULL, { 0x01, 0x7B, 0x34, 0x72, 0xC1, 0xDE, 0xE3, 0x04, 0xFA, 0x0B, 0x2F, 0xF8, 0xE5, 0x3F, 0xF7, 0xD6 } }, // 99 WOW-23910patch7.2.5_PTR legion_72_tsf { 0xBAE9F621B60174F1ULL, { 0x38, 0xC3, 0xFB, 0x39, 0xB4, 0x97, 0x17, 0x60, 0xB4, 0xB9, 0x82, 0xFE, 0x9F, 0x09, 0x50, 0x14 } }, // 100 WOW-24727patch7.3.0_PTR Rejection of the Gift cinematic (legion_73_agi) { 0x34DE1EEADC97115EULL, { 0x2E, 0x3A, 0x53, 0xD5, 0x9A, 0x49, 0x1E, 0x5C, 0xD1, 0x73, 0xF3, 0x37, 0xF7, 0xCD, 0x8C, 0x61 } }, // 101 WOW-24727patch7.3.0_PTR Resurrection of Alleria Windrunner cinematic (legion_73_avt) { 0xE07E107F1390A3DFULL, { 0x29, 0x0D, 0x27, 0xB0, 0xE8, 0x71, 0xF8, 0xC5, 0xB1, 0x4A, 0x14, 0xE5, 0x14, 0xD0, 0xF0, 0xD9 } }, // 102 WOW-25079patch7.3.2_PTR Tottle battle pet, Raptor mount, Horse mount (104 files) { 0x32690BF74DE12530ULL, { 0xA2, 0x55, 0x62, 0x10, 0xAE, 0x54, 0x22, 0xE6, 0xD6, 0x1E, 0xDA, 0xAF, 0x12, 0x2C, 0xB6, 0x37 } }, // 103 WOW-24781patch7.3.0_PTR legion_73_pan { 0xBF3734B1DCB04696ULL, { 0x48, 0x94, 0x61, 0x23, 0x05, 0x0B, 0x00, 0xA7, 0xEF, 0xB1, 0xC0, 0x29, 0xEE, 0x6C, 0xC4, 0x38 } }, // 104 WOW-25079patch7.3.2_PTR legion_73_afn { 0x74F4F78002A5A1BEULL, { 0xC1, 0x4E, 0xEC, 0x8D, 0x5A, 0xEE, 0xF9, 0x3F, 0xA8, 0x11, 0xD4, 0x50, 0xB4, 0xE4, 0x6E, 0x91 } }, // 105 WOW-25079patch7.3.2_PTR SilithusPhase01 map // { 0x423F07656CA27D23ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 107 WOW-25600patch7.3.5_PTR bltestmap // { 0x0691678F83E8A75DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 108 WOW-25600patch7.3.5_PTR filedataid 1782602-1782603 // { 0x324498590F550556ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 109 WOW-25600patch7.3.5_PTR filedataid 1782615-1782619 // { 0xC02C78F40BEF5998ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 110 WOW-25600patch7.3.5_PTR test/testtexture.blp (fdid 1782613) // { 0x47011412CCAAB541ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 111 WOW-25600patch7.3.5_PTR unused in 25600 // { 0x23B6F5764CE2DDD6ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 112 WOW-25600patch7.3.5_PTR unused in 25600 // { 0x8E00C6F405873583ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 113 WOW-25600patch7.3.5_PTR filedataid 1783470-1783472 { 0x78482170E4CFD4A6ULL, { 0x76, 0x85, 0x40, 0xC2, 0x0A, 0x5B, 0x15, 0x35, 0x83, 0xAD, 0x7F, 0x53, 0x13, 0x0C, 0x58, 0xFE } }, // 114 WOW-25600patch7.3.5_PTR Magni Bronzebeard VO { 0xB1EB52A64BFAF7BFULL, { 0x45, 0x81, 0x33, 0xAA, 0x43, 0x94, 0x9A, 0x14, 0x16, 0x32, 0xC4, 0xF8, 0x59, 0x6D, 0xE2, 0xB0 } }, // 115 WOW-25600patch7.3.5_PTR dogmount, 50 files { 0xFC6F20EE98D208F6ULL, { 0x57, 0x79, 0x0E, 0x48, 0xD3, 0x55, 0x00, 0xE7, 0x0D, 0xF8, 0x12, 0x59, 0x4F, 0x50, 0x7B, 0xE7 } }, // 117 WOW-25632patch7.3.5_PTR shop stuff { 0x402CFABF2020D9B7ULL, { 0x67, 0x19, 0x7B, 0xCD, 0x9D, 0x0E, 0xF0, 0xC4, 0x08, 0x53, 0x78, 0xFA, 0xA6, 0x9A, 0x32, 0x64 } }, // 118 WOW-25678patch7.3.5_PTR filedataid 1854762 { 0x6FA0420E902B4FBEULL, { 0x27, 0xB7, 0x50, 0x18, 0x4E, 0x53, 0x29, 0xC4, 0xE4, 0x45, 0x5C, 0xBD, 0x3E, 0x1F, 0xD5, 0xAB } }, // 119 WOW-25744patch7.3.5_PTR legion_735_epa / legion_735_eph { 0x1076074F2B350A2DULL, { 0x88, 0xBF, 0x0C, 0xD0, 0xD5, 0xBA, 0x15, 0x9A, 0xE7, 0xCB, 0x91, 0x6A, 0xFB, 0xE1, 0x38, 0x65 } }, // 121 WOW-26287patch8.0.1_Beta skiff { 0x816F00C1322CDF52ULL, { 0x6F, 0x83, 0x22, 0x99, 0xA7, 0x57, 0x89, 0x57, 0xEE, 0x86, 0xB7, 0xF9, 0xF1, 0x5B, 0x01, 0x88 } }, // 122 WOW-26287patch8.0.1_Beta snowkid { 0xDDD295C82E60DB3CULL, { 0x34, 0x29, 0xCC, 0x59, 0x27, 0xD1, 0x62, 0x97, 0x65, 0x97, 0x4F, 0xD9, 0xAF, 0xAB, 0x75, 0x80 } }, // 123 WOW-26287patch8.0.1_Beta redbird { 0x83E96F07F259F799ULL, { 0x91, 0xF7, 0xD0, 0xE7, 0xA0, 0x2C, 0xDE, 0x0D, 0xE0, 0xBD, 0x36, 0x7F, 0xAB, 0xCB, 0x8A, 0x6E } }, // 124 WOW-26522patch8.0.1_Beta BlizzCon 2018 (Alliance and Horde banners and cloaks) { 0x49FBFE8A717F03D5ULL, { 0xC7, 0x43, 0x77, 0x70, 0xCF, 0x15, 0x3A, 0x31, 0x35, 0xFA, 0x6D, 0xC5, 0xE4, 0xC8, 0x5E, 0x65 } }, // 225 WOW-27826patch8.1.0_PTR Meatwagon mount (Warcraft 3: Reforged) { 0xC1E5D7408A7D4484ULL, { 0xA7, 0xD8, 0x8E, 0x52, 0x74, 0x9F, 0xA5, 0x45, 0x9D, 0x64, 0x45, 0x23, 0xF8, 0x35, 0x96, 0x51 } }, // 226 WOW-26871patch8.0.1_Beta Sylvanas Warbringer cinematic { 0xE46276EB9E1A9854ULL, { 0xCC, 0xCA, 0x36, 0xE3, 0x02, 0xF9, 0x45, 0x9B, 0x1D, 0x60, 0x52, 0x6A, 0x31, 0xBE, 0x77, 0xC8 } }, // 227 WOW-26871patch8.0.1_Beta ltc_a, ltc_h and ltt cinematics { 0xD245B671DD78648CULL, { 0x19, 0xDC, 0xB4, 0xD4, 0x5A, 0x65, 0x8B, 0x54, 0x35, 0x1D, 0xB7, 0xDD, 0xC8, 0x1D, 0xE7, 0x9E } }, // 228 WOW-26871patch8.0.1_Beta stz, zia, kta, jnm & ja cinematics { 0x4C596E12D36DDFC3ULL, { 0xB8, 0x73, 0x19, 0x26, 0x38, 0x94, 0x99, 0xCB, 0xD4, 0xAD, 0xBF, 0x50, 0x06, 0xCA, 0x03, 0x91 } }, // 229 WOW-26871patch8.0.1_Beta bar cinematic { 0x0C9ABD5081C06411ULL, { 0x25, 0xA7, 0x7C, 0xD8, 0x00, 0x19, 0x7E, 0xE6, 0xA3, 0x2D, 0xD6, 0x3F, 0x04, 0xE1, 0x15, 0xFA } }, // 230 WOW-26871patch8.0.1_Beta zcf cinematic { 0x3C6243057F3D9B24ULL, { 0x58, 0xAE, 0x3E, 0x06, 0x42, 0x10, 0xE3, 0xED, 0xF9, 0xC1, 0x25, 0x9C, 0xDE, 0x91, 0x4C, 0x5D } }, // 231 WOW-26871patch8.0.1_Beta ktf cinematic { 0x7827FBE24427E27DULL, { 0x34, 0xA4, 0x32, 0x04, 0x20, 0x73, 0xCD, 0x0B, 0x51, 0x62, 0x70, 0x68, 0xD2, 0xE0, 0xBD, 0x3E } }, // 232 WOW-26871patch8.0.1_Beta rot cinematic { 0xFAF9237E1186CF66ULL, { 0xAE, 0x78, 0x78, 0x40, 0x04, 0x1E, 0x9B, 0x41, 0x98, 0xF4, 0x79, 0x71, 0x4D, 0xAD, 0x56, 0x2C } }, // 233 WOW-28048patch8.1.0_PTR DB2 partial encryption test battle pet // { 0x5DD92EE32BBF9ABDULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 234 WOW-27004patch8.0.1_Subm filedataid 2238294 { 0x0B68A7AF5F85F7EEULL, { 0x27, 0xAA, 0x01, 0x10, 0x82, 0xF5, 0xE8, 0xBB, 0xBD, 0x71, 0xD1, 0xBA, 0x04, 0xF6, 0xAB, 0xA4 } }, // 236 WOW-28151patch8.1.0_PTR encrypted06 Flying pig mount // { 0x01531713C83FCC39ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 237 WOW-28151patch8.1.0_PTR fdid 2460009, 2460732 { 0x76E4F6739A35E8D7ULL, { 0x05, 0xCF, 0x27, 0x67, 0x22, 0xE7, 0x16, 0x5C, 0x5A, 0x4F, 0x65, 0x95, 0x25, 0x6A, 0x0B, 0xFB } }, // 238 WOW-28294patch8.1.0_PTR Sylverian Dreamer mount { 0x66033F28DC01923CULL, { 0x9F, 0x95, 0x19, 0x86, 0x14, 0x90, 0xC5, 0xA9, 0xFF, 0xD4, 0xD8, 0x2A, 0x6D, 0x00, 0x67, 0xDB } }, // 239 WOW-28294patch8.1.0_PTR Vulpine Familiar mount { 0xFCF34A9B05AE7E6AULL, { 0xE7, 0xC2, 0xC8, 0xF7, 0x7E, 0x30, 0xAC, 0x24, 0x0F, 0x39, 0xEC, 0x23, 0x97, 0x12, 0x96, 0xE5 } }, // 240 WOW-28151patch8.1.0_PTR Alliance fireworks { 0xE2F6BD41298A2AB9ULL, { 0xC5, 0xDC, 0x1B, 0xB4, 0x3B, 0x8C, 0xF3, 0xF0, 0x85, 0xD6, 0x98, 0x68, 0x26, 0xB9, 0x28, 0xEC } }, // 241 WOW-28151patch8.1.0_PTR Horde fireworks { 0x14C4257E557B49A1ULL, { 0x06, 0x4A, 0x97, 0x09, 0xF4, 0x2D, 0x50, 0xCB, 0x5F, 0x8B, 0x94, 0xBC, 0x1A, 0xCF, 0xDD, 0x5D } }, // 242 WOW-28440patch8.1.0_PTR dor cinematic { 0x1254E65319C6EEFFULL, { 0x79, 0xD2, 0xB3, 0xD1, 0xCC, 0xB0, 0x15, 0x47, 0x4E, 0x71, 0x58, 0x81, 0x38, 0x64, 0xB8, 0xE6 } }, // 243 WOW-28440patch8.1.0_PTR akt cinematic { 0xC8753773ADF1174CULL, { 0x1E, 0x0E, 0x37, 0xD4, 0x2E, 0xE5, 0xCE, 0x5E, 0x80, 0x67, 0xF0, 0x39, 0x4B, 0x09, 0x05, 0xF2 } }, // 244 WOW-28938patch8.1.5_PTR Obsidian Worldbreaker mount & Lil' Nefarian pet { 0x2170BCAA9FA96E22ULL, { 0x6D, 0xDA, 0x6D, 0x48, 0xD7, 0x2D, 0xC8, 0x00, 0x5D, 0xB9, 0xDC, 0x15, 0x36, 0x8D, 0x35, 0xBC } }, // 245 WOW-28938patch8.1.5_PTR baby alpaca pet // { 0x75485627AA225F4DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 246 WOW-28938patch8.1.5_PTR fdid 2741546, 2741548, 2741549 { 0x08717B15BF3C7955ULL, { 0x4B, 0x06, 0xBF, 0x9D, 0x17, 0x66, 0x3C, 0xEB, 0x33, 0x12, 0xEA, 0x3C, 0x69, 0xFB, 0xC5, 0xDD } }, // 248 WOW-29220patch8.1.5_PTR inv_encrypted20.blp (fdid 2823166) // { 0xD19DCF7ACA8D96D6ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 249 WOW-30080patch8.2.0_PTR starts at fdid 2843110, 10 files { 0x9FD609902B4B2E07ULL, { 0xAB, 0xE0, 0xC5, 0xF9, 0xC1, 0x23, 0xE6, 0xE2, 0x4E, 0x7B, 0xEA, 0x43, 0xC2, 0xBF, 0x00, 0xAC } }, // 250 WOW-29418patch8.1.5_PTR Derek Proudmoore cinematic (dpr, 5 files) // { 0xCB26B441FAE4C8CDULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 251 WOW-30080patch8.2.0_PTR fdid 2888623, 2892270, 2892271, 2892272, 2892274, 2892275 { 0xA98C7594F55C02F0ULL, { 0xEE, 0xDB, 0x77, 0x47, 0x3B, 0x72, 0x1D, 0xED, 0x62, 0x04, 0xA9, 0x76, 0xC9, 0xA6, 0x61, 0xE7 } }, // 252 WOW-30080patch8.2.0_PTR BlizzCon 2019 - Murloc pets { 0x259EE68CD9E76DBAULL, { 0x46, 0x5D, 0x78, 0x4F, 0x10, 0x19, 0x66, 0x1C, 0xCF, 0x41, 0x7F, 0xE4, 0x66, 0x80, 0x12, 0x83 } }, // 253 WOW-30080patch8.2.0_PTR Alabaster mounts (30 files) { 0x6A026290FBDB3754ULL, { 0x3D, 0x2D, 0x62, 0x08, 0x50, 0xA6, 0x76, 0x5D, 0xD5, 0x91, 0x22, 0x4F, 0x60, 0x5B, 0x94, 0x9A } }, // 255 WOW-30080patch8.2.0_PTR BlizzCon 2019 - Wendigo transmog set { 0xCF72FD04608D36EDULL, { 0xA0, 0xA8, 0x89, 0x97, 0x6D, 0x02, 0xFA, 0x8D, 0x00, 0xF7, 0xAF, 0x00, 0x17, 0xAD, 0x72, 0x1F } }, // 257 WOW-30262patch8.2.0_PTR Azshara Warbringer cinematic (5 files) { 0x17F07C2E3A45DB3DULL, { 0x6D, 0x38, 0x86, 0xBD, 0xB9, 0x1E, 0x71, 0x5A, 0xE7, 0x18, 0x2D, 0x9F, 0x3A, 0x08, 0xF2, 0xC9 } }, // 258 WOW-30262patch8.2.0_PTR Solesa Naksu Nazjatar phase (34 files) { 0xDFAB5841B87802B5ULL, { 0xF3, 0x7E, 0x96, 0xED, 0x8A, 0x1F, 0x8D, 0x85, 0x2F, 0x07, 0x5D, 0xDE, 0x37, 0xC7, 0x13, 0x27 } }, // 259 WOW-31337patch8.2.5_PTR Ratmount2 Flying Rat Mount { 0xC050FA06BB0538F6ULL, { 0xC5, 0x52, 0xF5, 0xD0, 0xB7, 0x22, 0x31, 0x50, 0x2D, 0x25, 0x47, 0x31, 0x4E, 0x60, 0x15, 0xF7 } }, // 260 WOW-30495patch8.2.0_PTR Crossroads cinematic (5 files) { 0xAB5CDD3FC321831FULL, { 0xE1, 0x38, 0x4F, 0x5B, 0x06, 0xEB, 0xBC, 0xD3, 0x33, 0x69, 0x5A, 0xA6, 0xFF, 0xC6, 0x83, 0x18 } }, // 261 WOW-30495patch8.2.0_PTR Azshara kill cinematic (5 files) { 0xA7B7D1F12395040EULL, { 0x36, 0xAD, 0x3B, 0x31, 0x27, 0x3F, 0x1E, 0xBC, 0xEE, 0x85, 0x20, 0xAA, 0xA7, 0x4B, 0x12, 0xF2 } }, // 262 WOW-30495patch8.2.0_PTR Nazjatar intro cinematics (9 files) { 0x83A2AB72DD8AE992ULL, { 0x02, 0x3C, 0xFF, 0x06, 0x2B, 0x19, 0xA5, 0x29, 0xB9, 0xF1, 0x4F, 0x9B, 0x7A, 0xAA, 0xC5, 0xBB } }, // 263 WOW-31337patch8.2.5_PTR 8.2.5 War Campaign scenario/models { 0xBEAF567CC45362F0ULL, { 0x8B, 0xD3, 0xED, 0x79, 0x24, 0x05, 0xD9, 0xEE, 0x74, 0x2B, 0xF6, 0xAF, 0xA9, 0x44, 0x57, 0x8A } }, // 264 WOW-31337patch8.2.5_PTR 8.2.5 War Campaign quests/vo { 0x7BB3A77FD8D14783ULL, { 0x4C, 0x94, 0xE3, 0x60, 0x9C, 0xFE, 0x0A, 0x82, 0x00, 0x0A, 0x0B, 0xD4, 0x60, 0x69, 0xAC, 0x6F } }, // 265 WOW-31337patch8.2.5_PTR 8.2.5 War Campaign epilogue quests { 0x8F4098E2470FE0C8ULL, { 0xAA, 0x71, 0x8D, 0x1F, 0x1A, 0x23, 0x07, 0x8D, 0x49, 0xAD, 0x0C, 0x60, 0x6A, 0x72, 0xF3, 0xD5 } }, // 266 WOW-31337patch8.2.5_PTR 8.2.5 War Campaign epilogue in-game cinematic5 { 0x6AC5C837A2027A6BULL, { 0xB0, 0xB7, 0xCE, 0x09, 0x17, 0x63, 0xD1, 0x5E, 0x7F, 0x69, 0xA8, 0xE2, 0x34, 0x2C, 0xDD, 0x7C } }, // 267 WOW-31337patch8.2.5_PTR Shadowlands CE rewards { 0x302AAD8B1F441D95ULL, { 0x24, 0xB8, 0x64, 0x38, 0xCF, 0x02, 0x53, 0x86, 0x49, 0xE5, 0xBA, 0x67, 0x2F, 0xD5, 0x99, 0x3A } }, // 271 WOW-31337patch8.2.5_PTR RaF mounts & armor // { 0x5C909F00088734B9ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 272 WOW-31337patch8.2.5_PTR Unused in 8.2.5 { 0xF785977C76DE9C77ULL, { 0x7F, 0x3C, 0x19, 0x51, 0xF5, 0x28, 0x3A, 0x18, 0xC1, 0xC6, 0xD4, 0x5B, 0x68, 0x67, 0xB5, 0x1A } }, // 273 WOW-31337patch8.2.5_PTR Starts at fdid 3071600, 313 files, Winter Veil? { 0x1CDAF3931871BEC3ULL, { 0x66, 0xB4, 0xD3, 0x4A, 0x3A, 0xF3, 0x0E, 0x5E, 0xB7, 0xF4, 0x14, 0xF6, 0xC3, 0x0A, 0xAF, 0x4F } }, // 275 WOW-31337patch8.2.5_PTR Winter Veil 2019 toy/achievement (28 files) { 0x814E1AB43F3F9345ULL, { 0xB6, 0x5E, 0x2A, 0x63, 0xA1, 0x16, 0xAA, 0x25, 0x1F, 0xA5, 0xD7, 0xB0, 0xBA, 0xAB, 0xF7, 0x78 } }, // 276 WOW-31599patch8.2.5_PTR The Negotiation cinematic (5 files) { 0x1FBE97A317FFBEFAULL, { 0xBD, 0x71, 0xF7, 0x8D, 0x43, 0x11, 0x7C, 0x68, 0x72, 0x4B, 0xB6, 0xE0, 0xD9, 0x57, 0x7E, 0x08 } }, // 277 WOW-31599patch8.2.5_PTR Reckoning cinematic (5 files) // { 0x30581F81528FB27CULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 278 WOW-32044patch8.3.0_PTR Contains creature that uses monkey sounds // { 0x4287F49A5BB366DAULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 279 WOW-31599patch8.2.5_PTR Unused in 8.2.5 { 0xD134F430A45C1CF2ULL, { 0x54, 0x3D, 0xA7, 0x84, 0xD4, 0xBD, 0x24, 0x28, 0xCF, 0xB5, 0xEB, 0xFE, 0xBA, 0x76, 0x2A, 0x90 } }, // 280 WOW-32044patch8.3.0_PTR Encrypted map w/ Icecrown assets, Darion Mograine VO // { 0x01C82EE0725EDA3AULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 281 WOW-31812patch8.2.5_PTR Unused in 8.2.5 // { 0x04C0C50B5BE0CC78ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 282 WOW-31812patch8.2.5_PTR Unused in 8.2.5 // { 0xA26FD104489B3DE5ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 283 WOW-31812patch8.2.5_PTR Unused in 8.2.5 // { 0xEA6C3B8F210A077FULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 284 WOW-32044patch8.3.0_PTR 18 files, 3159888 is a creature that uses gronn sounds, likely a mount // { 0x4A738212694AD0B6ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 285 WOW-32044patch8.3.0_PTR Unused in 32044 // { 0x2A430C60DDCC75FFULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 286 WOW-32044patch8.3.0_PTR 254 files, appears to be items { 0x0A096FB251CFF471ULL, { 0x05, 0xC7, 0x59, 0x12, 0xEC, 0xFF, 0x04, 0x0F, 0x85, 0xFB, 0x46, 0x97, 0xC9, 0x9C, 0x77, 0x03 } }, // 287 WOW-32414patch8.3.0_PTR In-game scene & VO // { 0x205AFFCDFBA639CBULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 288 WOW-32414patch8.3.0_PTR { 0x32B62CF10571971FULL, { 0x18, 0xB8, 0x3F, 0xDD, 0x5E, 0x4B, 0x39, 0x7F, 0xB8, 0x9B, 0xB5, 0x72, 0x46, 0x75, 0xCC, 0xBA } }, // 289 WOW-32489patch8.3.0_PTR In-game Wrathion scene // { 0xB408D6CDE8E0D4C1ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 290 WOW-33978patch9.0.1_Beta { 0x1DBE03EF5A0059E1ULL, { 0xD6, 0x3B, 0x26, 0x3C, 0xB1, 0xC7, 0xE8, 0x56, 0x23, 0xCC, 0x42, 0x58, 0x79, 0xCC, 0x59, 0x2D } }, // 294 WOW-32489patch8.3.0_PTR Cinematic { 0x29D08CEA080FDB84ULL, { 0x06, 0x51, 0x32, 0xA6, 0x42, 0x8B, 0x19, 0xDF, 0xCB, 0x2B, 0x68, 0x94, 0x8B, 0xE9, 0x58, 0xF5 } }, // 295 WOW-32489patch8.3.0_PTR Cinematic { 0x3FE91B3FD7F18B37ULL, { 0xC9, 0x13, 0xB1, 0xC2, 0x0D, 0xAE, 0xC8, 0x04, 0xE9, 0xF8, 0xD3, 0x52, 0x7F, 0x2A, 0x05, 0xF7 } }, // 296 WOW-32489patch8.3.0_PTR Cinematic // { 0xF7BECC6682B9EF36ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 297 WOW-33978patch9.0.1_Beta // { 0xDCB5C5DC78520BD6ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 298 WOW-33978patch9.0.1_Beta // { 0x566DF4A5A9E3341FULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 299 WOW-33978patch9.0.1_Beta // { 0x9183F8AAA603704DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 300 WOW-33978patch9.0.1_Beta // { 0x856D38B447512C51ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 301 WOW-33978patch9.0.1_Beta // { 0x1D0614B43A9D6DF9ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 302 WOW-33978patch9.0.1_Beta { 0x00179EB433442A73ULL, { 0xD4, 0x1D, 0x69, 0x4C, 0x0D, 0xF8, 0x63, 0x10, 0x5C, 0x53, 0x49, 0xFD, 0x4F, 0xFB, 0xC2, 0x56 } }, // WOW-33978patch9.0.1_Beta { 0x033DBFE564685CF6ULL, { 0xA1, 0xE3, 0xB2, 0x4B, 0xFE, 0x85, 0x78, 0xC0, 0x3D, 0x0F, 0xCC, 0x7F, 0x19, 0xE6, 0xD0, 0x69 } }, // WOW-33978patch9.0.1_Beta { 0x05EC085870DF2AF0ULL, { 0x31, 0xE6, 0xAB, 0x03, 0xA0, 0x9E, 0x68, 0xC8, 0xFB, 0x55, 0xBF, 0x19, 0x5E, 0x1F, 0x1B, 0x99 } }, // WOW-33978patch9.0.1_Beta { 0x0626C885AF0EEF65ULL, { 0x55, 0x7E, 0xC9, 0x97, 0xAD, 0x64, 0x62, 0x1C, 0xFE, 0x2B, 0x3D, 0x79, 0xA5, 0xB7, 0x50, 0x65 } }, // WOW-33978patch9.0.1_Beta { 0x070994B8FE881897ULL, { 0xA5, 0x4A, 0x65, 0x23, 0x5D, 0x6D, 0x64, 0x87, 0x27, 0x7C, 0x36, 0x10, 0xBA, 0x84, 0xAB, 0xF5 } }, // WOW-33978patch9.0.1_Beta { 0x0811E03FCFB84903ULL, { 0x6E, 0x56, 0x77, 0x05, 0x1E, 0xE0, 0x92, 0xA5, 0x7C, 0xF6, 0x56, 0xE1, 0xC5, 0x00, 0x2F, 0xFC } }, // WOW-33978patch9.0.1_Beta { 0x0993C8311127F20EULL, { 0xE9, 0x53, 0xC3, 0x57, 0x4C, 0x3E, 0xE5, 0x43, 0xD9, 0xF5, 0x52, 0xED, 0xC8, 0x2F, 0x20, 0xE9 } }, // WOW-33978patch9.0.1_Beta { 0x0A5E25A024FBA6B1ULL, { 0xBB, 0x8F, 0x7B, 0x18, 0x15, 0x09, 0xA9, 0x31, 0x7B, 0xE1, 0x59, 0x26, 0x34, 0x62, 0xF2, 0x8D } }, // WOW-33978patch9.0.1_Beta { 0x0C15F03D0FE27E48ULL, { 0x74, 0x33, 0x79, 0x1B, 0xF3, 0x45, 0x89, 0xEB, 0x64, 0x32, 0x99, 0x7E, 0x10, 0xA2, 0x54, 0x22 } }, // WOW-33978patch9.0.1_Beta { 0x0C2D617E60368120ULL, { 0x94, 0xB8, 0x12, 0x27, 0x1D, 0x6D, 0xE6, 0xC3, 0x18, 0x14, 0x40, 0xD0, 0xAA, 0x27, 0x65, 0x3D } }, // WOW-33978patch9.0.1_Beta { 0x0C49BD78283973B0ULL, { 0x34, 0xB5, 0xBC, 0x69, 0xC1, 0x2A, 0xA3, 0x7A, 0xD7, 0x12, 0x9B, 0x7E, 0x28, 0xC1, 0xDA, 0x58 } }, // WOW-33978patch9.0.1_Beta { 0x0CA696AE84A34281ULL, { 0x9E, 0x0C, 0xBF, 0x53, 0x20, 0x5A, 0x20, 0x64, 0x3A, 0x92, 0x30, 0xE9, 0x4E, 0xCB, 0x0E, 0x3E } }, // WOW-33978patch9.0.1_Beta { 0x0D069101292C9EF9ULL, { 0x11, 0x6A, 0x8D, 0x4C, 0x1B, 0xA6, 0x65, 0xE1, 0x55, 0x52, 0xCA, 0x69, 0xCD, 0x7C, 0xB3, 0x67 } }, // WOW-33978patch9.0.1_Beta { 0x0DE2CF914415467AULL, { 0xAF, 0x90, 0xC0, 0x00, 0x65, 0x47, 0xCA, 0x72, 0x72, 0x34, 0xC7, 0xAF, 0xA3, 0xAB, 0xA8, 0xA7 } }, // WOW-33978patch9.0.1_Beta { 0x0DFACE33557D5092ULL, { 0x61, 0x54, 0x99, 0x41, 0x94, 0x26, 0x6E, 0xEF, 0x93, 0xD7, 0x5F, 0x2C, 0x23, 0xEA, 0x52, 0xB5 } }, // WOW-33978patch9.0.1_Beta { 0x0FC6144903CFC12BULL, { 0x3B, 0x15, 0xF8, 0x9E, 0x91, 0x2F, 0xE4, 0x3D, 0x89, 0x64, 0x3D, 0xCB, 0xA9, 0x45, 0xE5, 0xCD } }, // WOW-33978patch9.0.1_Beta { 0x100C57C7D8B42E58ULL, { 0x24, 0xF7, 0x54, 0x23, 0x09, 0x73, 0x46, 0xC7, 0x92, 0x7F, 0x14, 0x51, 0x93, 0xB6, 0xD5, 0xFE } }, // WOW-33978patch9.0.1_Beta { 0x1108F594E9F5BCB7ULL, { 0x47, 0x80, 0xC7, 0xB3, 0x7A, 0x42, 0xFA, 0x64, 0xD4, 0x90, 0xFB, 0xB6, 0xDE, 0xE9, 0x30, 0xDC } }, // WOW-33978patch9.0.1_Beta { 0x13DA86340D87FEB9ULL, { 0x55, 0x24, 0xD9, 0x49, 0x18, 0x52, 0x37, 0x1F, 0x19, 0x15, 0xCC, 0x1F, 0xDF, 0xD0, 0x53, 0xD1 } }, // WOW-33978patch9.0.1_Beta { 0x141A5383D3AEEB59ULL, { 0xD5, 0xDF, 0xE7, 0x38, 0x99, 0xD2, 0x86, 0xE3, 0x02, 0x2F, 0xAC, 0xAD, 0x3E, 0x70, 0x4B, 0xE3 } }, // WOW-33978patch9.0.1_Beta { 0x1423888694CDE5FCULL, { 0xE7, 0xFA, 0x1C, 0xD3, 0x4A, 0xEA, 0xB1, 0xB6, 0x1B, 0x0A, 0x6C, 0x9E, 0xA6, 0xA9, 0x04, 0x08 } }, // WOW-33978patch9.0.1_Beta { 0x153616FF38D5E460ULL, { 0x0A, 0x94, 0x08, 0xA4, 0x9A, 0x1C, 0xE0, 0x5B, 0x90, 0x30, 0x4D, 0x01, 0x7D, 0xE7, 0x58, 0x92 } }, // WOW-33978patch9.0.1_Beta { 0x15D5A13976DCBD05ULL, { 0xD1, 0x7D, 0x19, 0xEE, 0x14, 0xF7, 0xDD, 0x88, 0x0F, 0x0E, 0xF6, 0xCE, 0x64, 0x44, 0x54, 0xBE } }, // WOW-33978patch9.0.1_Beta { 0x15FA2DA0B33C39EEULL, { 0xA2, 0x94, 0x10, 0xBB, 0x93, 0x1D, 0xA3, 0xE7, 0x84, 0x85, 0x96, 0xFB, 0xA7, 0x7F, 0x50, 0x37 } }, // WOW-33978patch9.0.1_Beta { 0x17ABC9D27E24B4D7ULL, { 0xFA, 0x41, 0x45, 0x70, 0x62, 0x60, 0x5A, 0x17, 0x3D, 0x10, 0xFB, 0x90, 0xE0, 0x77, 0x9A, 0x44 } }, // WOW-33978patch9.0.1_Beta { 0x188E37F5AB501082ULL, { 0xE5, 0x66, 0x96, 0x36, 0x09, 0x3C, 0xE1, 0xB8, 0x99, 0x75, 0x6B, 0xE6, 0x9B, 0x8C, 0x2C, 0x58 } }, // WOW-33978patch9.0.1_Beta { 0x18C32E5601F89AC3ULL, { 0xC8, 0x64, 0x03, 0x2D, 0x3E, 0x9D, 0xA9, 0x29, 0xF9, 0xFE, 0xC6, 0xE5, 0xB5, 0x38, 0x94, 0xA1 } }, // WOW-33978patch9.0.1_Beta { 0x1A16C07D5F35124FULL, { 0x34, 0xAB, 0xF6, 0xA4, 0x68, 0x69, 0xE0, 0x0C, 0xBB, 0x83, 0x94, 0x7C, 0xFD, 0x6A, 0x39, 0x47 } }, // WOW-33978patch9.0.1_Beta { 0x1A4F317C88C93C04ULL, { 0xFF, 0x16, 0x9D, 0x5A, 0xB8, 0x13, 0x5D, 0x38, 0xEC, 0x92, 0xFE, 0xAB, 0xEE, 0x0E, 0x39, 0x7B } }, // WOW-33978patch9.0.1_Beta { 0x1ABF6A7265BBC7AFULL, { 0x9E, 0x12, 0x7C, 0xE7, 0x16, 0x69, 0x1C, 0x50, 0xB5, 0xD4, 0x95, 0x9D, 0xAD, 0xA8, 0xEB, 0xEC } }, // WOW-33978patch9.0.1_Beta { 0x1BD281160FB552FDULL, { 0xEA, 0x57, 0x6F, 0x20, 0xAA, 0x9A, 0xF5, 0x8A, 0x76, 0xF4, 0xB8, 0x43, 0xF5, 0x3A, 0x8E, 0x5D } }, // WOW-33978patch9.0.1_Beta { 0x1BE9A4EEC5B455C5ULL, { 0xBA, 0xFC, 0x5C, 0xC3, 0x9B, 0xDD, 0xC1, 0xD8, 0x02, 0xBA, 0x07, 0x46, 0x41, 0x42, 0x7D, 0x68 } }, // WOW-33978patch9.0.1_Beta { 0x1DCF36E171124EFCULL, { 0xC6, 0x59, 0x7A, 0xA9, 0x76, 0x98, 0x8B, 0xD4, 0xCF, 0x57, 0xB8, 0x33, 0x04, 0xDA, 0x19, 0x34 } }, // WOW-33978patch9.0.1_Beta { 0x1E47BD8DB4D6032FULL, { 0x0F, 0x81, 0x0C, 0x27, 0xE9, 0x10, 0xA5, 0xFF, 0xD1, 0x47, 0x79, 0x27, 0xE2, 0x40, 0x64, 0x59 } }, // WOW-33978patch9.0.1_Beta { 0x1FB4F1C56721C87EULL, { 0x64, 0x8C, 0x26, 0x55, 0xE0, 0xAF, 0x50, 0xF4, 0x26, 0xC5, 0xCD, 0x2C, 0x4F, 0xD2, 0x21, 0xE7 } }, // WOW-33978patch9.0.1_Beta { 0x225902F0EC8BF0FCULL, { 0x09, 0x81, 0x25, 0xBC, 0x75, 0x9E, 0x47, 0xA4, 0x4F, 0x46, 0x7E, 0xCC, 0x28, 0x48, 0x07, 0x47 } }, // WOW-33978patch9.0.1_Beta { 0x225EADE089BA38D8ULL, { 0xFD, 0x5D, 0x6A, 0x9A, 0xDA, 0x76, 0xC7, 0x06, 0x7A, 0x83, 0xBF, 0x5E, 0x56, 0x92, 0xC6, 0xC3 } }, // WOW-33978patch9.0.1_Beta { 0x22EE6101A078F310ULL, { 0xEC, 0x43, 0x74, 0x15, 0xFA, 0x5B, 0xC5, 0x98, 0x79, 0xBB, 0xAA, 0x5C, 0x5F, 0xF3, 0x6A, 0xCD } }, // WOW-33978patch9.0.1_Beta { 0x23457E4AB5352E38ULL, { 0xEE, 0x3F, 0xDC, 0xA7, 0x8B, 0x42, 0xC0, 0x75, 0x03, 0xEF, 0x98, 0x86, 0xF9, 0x8B, 0xB0, 0x28 } }, // WOW-33978patch9.0.1_Beta { 0x236B38CBE43FC318ULL, { 0x14, 0x89, 0xB7, 0x05, 0x62, 0xD5, 0x6A, 0x60, 0x76, 0x00, 0xFA, 0xA8, 0x0A, 0x5A, 0xFD, 0xEF } }, // WOW-33978patch9.0.1_Beta { 0x264DB70A1A6CC720ULL, { 0x94, 0x88, 0x48, 0x13, 0x6B, 0xDD, 0x11, 0xD9, 0x95, 0xF3, 0xC4, 0x16, 0x9B, 0x8A, 0x9C, 0xAF } }, // WOW-33978patch9.0.1_Beta { 0x2678A2608A95FAAEULL, { 0xDB, 0x72, 0x2D, 0x47, 0x18, 0xB8, 0xB2, 0xC3, 0x13, 0x17, 0xB8, 0x43, 0xF6, 0x2D, 0x5F, 0xA8 } }, // WOW-33978patch9.0.1_Beta { 0x27683B8282673916ULL, { 0x9A, 0x06, 0x02, 0xC6, 0x62, 0x4A, 0xD2, 0xB1, 0x85, 0x18, 0x7C, 0x8B, 0xD7, 0xAF, 0x81, 0x7A } }, // WOW-33978patch9.0.1_Beta { 0x278A558C1E4C9789ULL, { 0x7C, 0xCF, 0xEE, 0xF6, 0x2E, 0xD5, 0x65, 0x60, 0xFD, 0x12, 0x94, 0xBB, 0x79, 0x7E, 0xC0, 0x0C } }, // WOW-33978patch9.0.1_Beta { 0x29C4474FA5A650C2ULL, { 0xB4, 0x5A, 0x3E, 0x43, 0xB8, 0x18, 0x52, 0xD2, 0x69, 0x70, 0x0D, 0x11, 0xCA, 0xAA, 0x52, 0x74 } }, // WOW-33978patch9.0.1_Beta { 0x29E553202112D688ULL, { 0xBB, 0x29, 0xC4, 0x2B, 0x23, 0xD1, 0x0E, 0xBE, 0x4E, 0xC7, 0x33, 0xC0, 0x4F, 0xB1, 0x9E, 0x0C } }, // WOW-33978patch9.0.1_Beta { 0x2A50EBBA4B44FBF5ULL, { 0x7F, 0x1F, 0xA1, 0x12, 0x4C, 0xC3, 0xF4, 0x70, 0xAD, 0x48, 0x30, 0xB6, 0xD6, 0xA1, 0xF5, 0x13 } }, // WOW-33978patch9.0.1_Beta { 0x2AFA1E90F5548DADULL, { 0x76, 0x6F, 0x2A, 0x52, 0x49, 0x29, 0xD9, 0x97, 0x83, 0x5D, 0xAE, 0x83, 0xC9, 0xB3, 0xDF, 0x4B } }, // WOW-33978patch9.0.1_Beta { 0x2CDD202075F2BB7BULL, { 0x8B, 0x78, 0x02, 0x28, 0xAD, 0x85, 0xC5, 0xB0, 0x26, 0x53, 0xB9, 0x0E, 0x6A, 0x98, 0x28, 0x44 } }, // WOW-33978patch9.0.1_Beta { 0x2D5511A1586B85BDULL, { 0x3A, 0x2A, 0x7A, 0x6B, 0x63, 0xE6, 0xA7, 0x0E, 0x26, 0x52, 0xDB, 0x45, 0x1C, 0x80, 0xA8, 0x78 } }, // WOW-33978patch9.0.1_Beta { 0x2D6ADB0D7D20DA8FULL, { 0x39, 0x5C, 0xD9, 0xDF, 0xCF, 0x86, 0xA5, 0xF5, 0x53, 0x1A, 0x1C, 0xA3, 0xDE, 0x43, 0x96, 0x04 } }, // WOW-33978patch9.0.1_Beta { 0x323E310D6BD63AC9ULL, { 0x52, 0x19, 0xD6, 0xB2, 0x0E, 0x8E, 0xA3, 0xF9, 0x72, 0xFD, 0x85, 0xD3, 0x55, 0xD3, 0x30, 0xD8 } }, // WOW-33978patch9.0.1_Beta { 0x337A168A21969F6CULL, { 0x67, 0x03, 0x67, 0x8A, 0x02, 0x29, 0x4E, 0x8E, 0x8B, 0xCF, 0xC4, 0x23, 0xF0, 0xFF, 0xF9, 0x35 } }, // WOW-33978patch9.0.1_Beta { 0x33801621A7C885FDULL, { 0x93, 0xBE, 0xD4, 0x71, 0xDD, 0x0C, 0xF9, 0x02, 0xA3, 0xA2, 0x43, 0xD8, 0xA3, 0x44, 0x11, 0xC5 } }, // WOW-33978patch9.0.1_Beta { 0x34487D8CF061757AULL, { 0x33, 0x26, 0x12, 0x28, 0xE6, 0x94, 0xDD, 0x95, 0x9A, 0xC9, 0xA6, 0x8B, 0x73, 0x6F, 0x6A, 0xF3 } }, // WOW-33978patch9.0.1_Beta { 0x35FC62C0E25BFA2CULL, { 0x69, 0x0D, 0x5E, 0xC1, 0xE5, 0x6A, 0xA0, 0x50, 0x49, 0x5B, 0x7B, 0xAE, 0x51, 0x3C, 0xAB, 0xCF } }, // WOW-33978patch9.0.1_Beta { 0x3692291361CDD66BULL, { 0x87, 0x81, 0x36, 0x91, 0xB2, 0xFA, 0xEA, 0xBC, 0x9A, 0x28, 0x26, 0x5B, 0xE0, 0x3F, 0xAE, 0x7E } }, // WOW-33978patch9.0.1_Beta { 0x38138436341EB55EULL, { 0xCD, 0x0D, 0xF8, 0x43, 0x30, 0x6C, 0x37, 0xB5, 0x17, 0x68, 0x91, 0xFC, 0x56, 0xDF, 0x7D, 0x57 } }, // WOW-33978patch9.0.1_Beta { 0x390336A9AC1ADBD3ULL, { 0xF1, 0x93, 0xE1, 0xAC, 0x0A, 0x1C, 0x80, 0x93, 0xF2, 0x31, 0x82, 0x47, 0xF1, 0xED, 0xC6, 0xA4 } }, // WOW-33978patch9.0.1_Beta { 0x41A0F110F49EB86AULL, { 0xDF, 0x50, 0xC9, 0x44, 0xB4, 0x2E, 0xF1, 0x6B, 0x4E, 0x6F, 0xA3, 0xE3, 0xF2, 0x8E, 0x70, 0xF7 } }, // WOW-33978patch9.0.1_Beta { 0x41BDEA4884539B22ULL, { 0x14, 0xC6, 0xE1, 0xA2, 0xC0, 0xBB, 0xCF, 0x1E, 0x6D, 0x21, 0xA2, 0x1F, 0x39, 0xD5, 0x51, 0xCA } }, // WOW-33978patch9.0.1_Beta { 0x41E698459DAD7101ULL, { 0xBF, 0xE8, 0x76, 0x01, 0x16, 0x60, 0xAD, 0x32, 0xA5, 0x72, 0x7E, 0x48, 0x9F, 0x83, 0x64, 0x68 } }, // WOW-33978patch9.0.1_Beta { 0x4223772617A3212AULL, { 0xC4, 0x09, 0x63, 0x17, 0x1B, 0x49, 0x6B, 0xB7, 0x65, 0xA5, 0x6E, 0xC3, 0xF2, 0x2D, 0xD6, 0x48 } }, // WOW-33978patch9.0.1_Beta { 0x422ABA4AE3E43AC5ULL, { 0xA0, 0xAE, 0x8D, 0xA9, 0x66, 0x39, 0x3F, 0x20, 0x60, 0x12, 0xA6, 0x1A, 0xE8, 0x19, 0xBD, 0xEA } }, // WOW-33978patch9.0.1_Beta { 0x429E445F8823DE47ULL, { 0xCB, 0x10, 0x47, 0x34, 0xDC, 0x4A, 0xDE, 0x45, 0x4A, 0x7A, 0x1A, 0x17, 0x96, 0x42, 0x3B, 0x21 } }, // WOW-33978patch9.0.1_Beta { 0x43855C4ABC59CD03ULL, { 0x91, 0xA3, 0x74, 0xB1, 0xE8, 0xCF, 0xE4, 0x6D, 0x2B, 0x82, 0xC7, 0x69, 0x52, 0x2D, 0x7F, 0xAF } }, // WOW-33978patch9.0.1_Beta { 0x44324FEB63BAD71BULL, { 0xBA, 0xA7, 0xA3, 0x31, 0x1F, 0x36, 0xD3, 0xF6, 0xC1, 0x58, 0x9F, 0xBF, 0x34, 0xF1, 0x63, 0xAF } }, // WOW-33978patch9.0.1_Beta { 0x445C444DDCB144D0ULL, { 0xE1, 0x1F, 0x30, 0x61, 0x10, 0xA2, 0x47, 0xAC, 0x08, 0x7E, 0x44, 0x66, 0xE8, 0x50, 0x5A, 0xC2 } }, // WOW-33978patch9.0.1_Beta { 0x4570E5C612AD4680ULL, { 0xC7, 0x5A, 0x72, 0x40, 0xBF, 0x08, 0x89, 0xBD, 0xB5, 0x0D, 0xB2, 0x14, 0x7C, 0xAE, 0xC2, 0x9F } }, // WOW-33978patch9.0.1_Beta { 0x463B8AC09861DBF2ULL, { 0x85, 0x14, 0x8A, 0x61, 0x6B, 0x3D, 0x2C, 0x0F, 0x3B, 0x50, 0x22, 0xB8, 0x71, 0xE4, 0xAB, 0x2F } }, // WOW-33978patch9.0.1_Beta { 0x46B9652048C5B9EBULL, { 0x42, 0xAD, 0x0B, 0xE5, 0x4B, 0xCF, 0xED, 0x59, 0x75, 0xC3, 0x5B, 0xC8, 0x00, 0xF6, 0x94, 0x4B } }, // WOW-33978patch9.0.1_Beta { 0x477AF884E1779F04ULL, { 0x81, 0xE7, 0x36, 0x61, 0x9B, 0x2A, 0xA8, 0xD3, 0xBE, 0xB3, 0x80, 0x01, 0x8C, 0x04, 0xC0, 0x97 } }, // WOW-33978patch9.0.1_Beta { 0x49E4DB410ACD71E4ULL, { 0xEC, 0xAD, 0x54, 0xE4, 0xCC, 0x6A, 0xE5, 0xA2, 0xB1, 0xCD, 0xFC, 0x94, 0xDC, 0xC4, 0xA7, 0xE0 } }, // WOW-33978patch9.0.1_Beta { 0x4BA04135745B0C77ULL, { 0x48, 0xAC, 0x8F, 0x63, 0xF9, 0xF6, 0xD4, 0xFC, 0x7B, 0xBE, 0xF2, 0xF8, 0x97, 0x40, 0xB7, 0x3F } }, // WOW-33978patch9.0.1_Beta { 0x4C3638A33B225E20ULL, { 0x85, 0xB3, 0x19, 0x7B, 0x10, 0x55, 0x03, 0xD9, 0xBC, 0x11, 0x76, 0xA6, 0xCE, 0x90, 0xB2, 0x1B } }, // WOW-33978patch9.0.1_Beta { 0x4CA3E38A5F339D85ULL, { 0x65, 0xDA, 0x28, 0xD6, 0x97, 0xE8, 0x2F, 0x7F, 0xB3, 0x26, 0x98, 0xF5, 0xDC, 0x13, 0xFE, 0x67 } }, // WOW-33978patch9.0.1_Beta { 0x4D356B23BBB63ABFULL, { 0x5B, 0x31, 0xF5, 0xD4, 0x37, 0x18, 0x20, 0xE0, 0x86, 0xD1, 0x59, 0x1F, 0x20, 0x96, 0x3C, 0x6F } }, // WOW-33978patch9.0.1_Beta { 0x4D81CA32AF4851B8ULL, { 0x2F, 0xAF, 0x21, 0x8D, 0x81, 0x0A, 0x5D, 0x2C, 0x5B, 0xCD, 0x6E, 0xBE, 0x7A, 0xF6, 0x7C, 0x66 } }, // WOW-33978patch9.0.1_Beta { 0x4E8FD51F7FDCB494ULL, { 0x6F, 0x19, 0x66, 0x48, 0xE9, 0xE0, 0xBD, 0x75, 0xC4, 0xC9, 0x63, 0x21, 0x4F, 0xBF, 0x43, 0x53 } }, // WOW-33978patch9.0.1_Beta { 0x4F29CD1DBFF6A704ULL, { 0x19, 0xF0, 0x45, 0x81, 0x27, 0x1E, 0xB2, 0x56, 0xF1, 0x05, 0x34, 0xE5, 0x0C, 0x82, 0x9D, 0x30 } }, // WOW-33978patch9.0.1_Beta { 0x50BDE6C6B138D920ULL, { 0x86, 0x3D, 0xE7, 0xEA, 0x7E, 0xDE, 0x52, 0x18, 0xCE, 0x92, 0x86, 0x36, 0x0D, 0xD5, 0x4C, 0xE3 } }, // WOW-33978patch9.0.1_Beta { 0x51C45371AA62A30DULL, { 0x80, 0x29, 0x74, 0xB8, 0x2D, 0x99, 0x83, 0xCD, 0x78, 0xB6, 0x65, 0x33, 0x20, 0xD3, 0xDA, 0xA5 } }, // WOW-33978patch9.0.1_Beta { 0x53422759826BCE28ULL, { 0x46, 0x7F, 0x7B, 0xB8, 0x45, 0xB5, 0x0A, 0xBE, 0x1B, 0x0B, 0xD7, 0x18, 0x95, 0xFD, 0x78, 0x38 } }, // WOW-33978patch9.0.1_Beta { 0x54E0E8C0E9A4CEE7ULL, { 0x41, 0x8D, 0x3F, 0xA2, 0xCB, 0xA3, 0xD9, 0x72, 0x29, 0x36, 0x1E, 0x2D, 0xE7, 0x96, 0x79, 0x28 } }, // WOW-33978patch9.0.1_Beta { 0x550AB6D42C0118D3ULL, { 0xBF, 0x50, 0x7B, 0x7E, 0xAC, 0x67, 0x29, 0x55, 0x0E, 0x87, 0xBD, 0x44, 0x4B, 0xB7, 0x0F, 0x35 } }, // WOW-33978patch9.0.1_Beta { 0x577E78AECCE3D388ULL, { 0x19, 0x6B, 0x59, 0x45, 0x39, 0x36, 0x4E, 0xCC, 0xFF, 0xD9, 0x2E, 0x71, 0xD9, 0x19, 0x30, 0x20 } }, // WOW-33978patch9.0.1_Beta { 0x587E8D3EB9594F4AULL, { 0x0E, 0xDE, 0x7A, 0x2C, 0x7B, 0xE8, 0xA1, 0x9A, 0x98, 0x73, 0x88, 0xC3, 0xF6, 0x04, 0x67, 0x55 } }, // WOW-33978patch9.0.1_Beta { 0x591C8211EE53BFDBULL, { 0x46, 0xA6, 0x19, 0xF7, 0xA6, 0xC4, 0xE0, 0xCE, 0x54, 0x2B, 0xB1, 0xA9, 0xE4, 0x71, 0x3F, 0x34 } }, // WOW-33978patch9.0.1_Beta { 0x597B3EA295C88FA7ULL, { 0x2D, 0x36, 0x4A, 0xE4, 0xD4, 0x6C, 0xEF, 0x42, 0x2A, 0x6F, 0x53, 0x1D, 0x9B, 0x1A, 0x7A, 0xD2 } }, // WOW-33978patch9.0.1_Beta { 0x59CBA5A49554EB2FULL, { 0x4B, 0x5F, 0xF9, 0xC6, 0x7E, 0x02, 0x1C, 0x5E, 0x2A, 0xA3, 0xB0, 0x5A, 0xC9, 0xE0, 0x50, 0x96 } }, // WOW-33978patch9.0.1_Beta { 0x5B1A3C7FC9D58D21ULL, { 0xA4, 0x8B, 0xA8, 0xE3, 0x19, 0x20, 0x7B, 0x2C, 0x04, 0xE8, 0x2B, 0x25, 0xC0, 0xF3, 0xC1, 0x49 } }, // WOW-33978patch9.0.1_Beta { 0x5B256C6230E168FFULL, { 0x89, 0xF3, 0x1D, 0x0F, 0xA3, 0x81, 0xD7, 0x23, 0x55, 0x56, 0x6B, 0x01, 0x24, 0xB8, 0xE1, 0xF2 } }, // WOW-33978patch9.0.1_Beta { 0x5B657AE4D191AAFBULL, { 0xF8, 0x84, 0xAA, 0xC1, 0xE2, 0xAB, 0x1C, 0x41, 0x3F, 0x7B, 0xAD, 0xDD, 0x14, 0x9D, 0x09, 0x61 } }, // WOW-33978patch9.0.1_Beta { 0x5B93089CD9316EFEULL, { 0x95, 0x76, 0x53, 0xD3, 0x4A, 0xBF, 0x29, 0x88, 0x46, 0xFF, 0x56, 0x04, 0xC1, 0xEB, 0x06, 0xD0 } }, // WOW-33978patch9.0.1_Beta { 0x5C172612CB60DEF2ULL, { 0xF5, 0x10, 0xBF, 0xEF, 0xFF, 0x2F, 0x87, 0x0B, 0x08, 0x93, 0x1B, 0x62, 0x52, 0x6F, 0x01, 0xFE } }, // WOW-33978patch9.0.1_Beta { 0x5E6EB3F5C47183FFULL, { 0x73, 0xEC, 0xB7, 0xAE, 0x15, 0x19, 0xE3, 0x9C, 0xC8, 0xBB, 0xD8, 0xF9, 0x90, 0xF3, 0xAC, 0xFB } }, // WOW-33978patch9.0.1_Beta { 0x5E7181FBA91F3766ULL, { 0x2F, 0x6A, 0xAB, 0xDC, 0x38, 0x9E, 0xA1, 0x0A, 0x11, 0xA9, 0xF1, 0x83, 0x58, 0x04, 0xF8, 0x6C } }, // WOW-33978patch9.0.1_Beta { 0x5E7B9DCD0092888BULL, { 0x6F, 0xE5, 0xA1, 0x40, 0x39, 0x93, 0xAE, 0x2A, 0x22, 0xA4, 0xF1, 0xAD, 0x31, 0x14, 0x4D, 0xAB } }, // WOW-33978patch9.0.1_Beta { 0x5F1C8C3CFABDC578ULL, { 0xEA, 0x5A, 0x36, 0x31, 0x46, 0x09, 0xC6, 0x70, 0x84, 0xDB, 0x63, 0xCF, 0xF7, 0x87, 0xDF, 0xCA } }, // WOW-33978patch9.0.1_Beta { 0x62EDE4DE7880CC68ULL, { 0xEA, 0xAA, 0x8D, 0x5C, 0x76, 0xEA, 0x95, 0x76, 0x31, 0x52, 0xAE, 0x50, 0x3C, 0xF5, 0x67, 0x45 } }, // WOW-33978patch9.0.1_Beta { 0x637AD198DBD985ECULL, { 0xA8, 0x31, 0xE0, 0x85, 0xC7, 0xA0, 0x98, 0x45, 0x57, 0xE7, 0x97, 0x30, 0x5C, 0xB2, 0xA2, 0x63 } }, // WOW-33978patch9.0.1_Beta { 0x6444A004E5F352B9ULL, { 0x76, 0x8A, 0x1C, 0x07, 0x02, 0x50, 0xD1, 0x70, 0x95, 0x02, 0x3D, 0xBD, 0xBD, 0x02, 0x3B, 0xC7 } }, // WOW-33978patch9.0.1_Beta { 0x649F6483822B8C3EULL, { 0xDB, 0xD9, 0x35, 0x20, 0x0A, 0x22, 0xE7, 0x89, 0x75, 0x89, 0x40, 0xE9, 0x1E, 0x1C, 0xFC, 0xAE } }, // WOW-33978patch9.0.1_Beta { 0x650E0558CFE68DBDULL, { 0xE4, 0x98, 0x29, 0x4E, 0x3A, 0x2C, 0xB7, 0xF1, 0x0B, 0x0F, 0x1A, 0xC8, 0x98, 0xF3, 0x79, 0xB6 } }, // WOW-33978patch9.0.1_Beta { 0x667C9AD6F57610C7ULL, { 0xF7, 0xF9, 0x8E, 0xFE, 0x5D, 0xB2, 0x27, 0x92, 0x8F, 0x97, 0x02, 0x85, 0x20, 0xD9, 0x7F, 0xC3 } }, // WOW-33978patch9.0.1_Beta { 0x67AD32C6DFF4B640ULL, { 0x03, 0x32, 0x11, 0xB8, 0x92, 0xF5, 0x2C, 0xBE, 0x56, 0x10, 0x51, 0xEB, 0xAA, 0xC8, 0xCE, 0x2F } }, // WOW-33978patch9.0.1_Beta { 0x6870068DF23BA71BULL, { 0x58, 0x97, 0x6C, 0xEA, 0xF5, 0xE2, 0xAB, 0x19, 0x38, 0x01, 0xD7, 0x13, 0xAB, 0xEC, 0x60, 0x5B } }, // WOW-33978patch9.0.1_Beta { 0x69CADB492EBF739EULL, { 0x63, 0xA4, 0x2B, 0xB5, 0x28, 0x05, 0x0C, 0x59, 0x47, 0x73, 0x5C, 0x18, 0x36, 0x60, 0x5C, 0x95 } }, // WOW-33978patch9.0.1_Beta { 0x6B20D9D506B930E6ULL, { 0xA4, 0x2E, 0xEC, 0xB7, 0x63, 0xC8, 0x79, 0xC3, 0x9C, 0xFC, 0xC3, 0x82, 0x0C, 0xC0, 0x57, 0x13 } }, // WOW-33978patch9.0.1_Beta { 0x6BF0360FC30A1651ULL, { 0x7A, 0x69, 0xDC, 0x9A, 0x2E, 0xF9, 0x6E, 0x9E, 0xFA, 0x1F, 0x4E, 0xE3, 0x75, 0x44, 0xD0, 0x60 } }, // WOW-33978patch9.0.1_Beta { 0x6C3380DB72AFCF88ULL, { 0xC5, 0xED, 0xF5, 0x55, 0xCE, 0x52, 0xF6, 0x71, 0x7B, 0x38, 0x28, 0x1C, 0x1B, 0x46, 0xCC, 0xDC } }, // WOW-33978patch9.0.1_Beta { 0x6CEAD213E31A6F01ULL, { 0x1D, 0x26, 0x3C, 0xFB, 0x4B, 0xD4, 0xDE, 0x9E, 0xBF, 0x59, 0xB1, 0x28, 0x7E, 0x39, 0x7F, 0xFE } }, // WOW-33978patch9.0.1_Beta { 0x6D6AD51EAA144766ULL, { 0x3E, 0xDD, 0x69, 0x0A, 0x09, 0x18, 0xA4, 0x2B, 0x49, 0xCB, 0xE2, 0x14, 0xBC, 0x90, 0x5D, 0x22 } }, // WOW-33978patch9.0.1_Beta { 0x6E92CDCE4FEA3B27ULL, { 0xE9, 0x88, 0x05, 0xA6, 0xB0, 0xD4, 0xEA, 0x3C, 0x28, 0x0A, 0x4E, 0xF4, 0x9A, 0xE5, 0x10, 0x11 } }, // WOW-33978patch9.0.1_Beta { 0x6EA6B9E529B29CC8ULL, { 0x7D, 0x8F, 0xE7, 0x88, 0xCA, 0x9A, 0xF0, 0x7F, 0x99, 0xC3, 0xDC, 0xA6, 0x1E, 0x1F, 0xBA, 0xC7 } }, // WOW-33978patch9.0.1_Beta { 0x72337416FD82C794ULL, { 0xF4, 0x78, 0x2D, 0x77, 0xEA, 0xDA, 0x33, 0x0A, 0x2D, 0xD1, 0x78, 0x9D, 0x64, 0x7F, 0x27, 0xBF } }, // WOW-33978patch9.0.1_Beta { 0x725E3CA857E0D99FULL, { 0x91, 0xD3, 0xAB, 0xCC, 0xDE, 0xE7, 0x20, 0xF4, 0xBA, 0xD5, 0xB9, 0x0E, 0xFA, 0xB9, 0x04, 0xFC } }, // WOW-33978patch9.0.1_Beta { 0x747A16FD3A3F6970ULL, { 0x16, 0x26, 0xFE, 0x4D, 0x37, 0x25, 0xE1, 0xD1, 0x7D, 0xEE, 0xE1, 0xC2, 0xDF, 0xEE, 0x85, 0xD4 } }, // WOW-33978patch9.0.1_Beta { 0x75BD0F89A7DF7076ULL, { 0x9A, 0x4F, 0x51, 0xF7, 0xDB, 0x21, 0xD0, 0x49, 0x32, 0xAF, 0x81, 0x86, 0x42, 0xCC, 0x7B, 0xEC } }, // WOW-33978patch9.0.1_Beta { 0x75E2B6A4145B00DBULL, { 0xE0, 0x9B, 0x5A, 0x79, 0xB5, 0xFB, 0xAD, 0xE1, 0x9D, 0x1D, 0x97, 0x9B, 0x2C, 0x32, 0xE4, 0xB7 } }, // WOW-33978patch9.0.1_Beta { 0x77F0A6FE6BE42E62ULL, { 0x35, 0xBA, 0xB9, 0x29, 0x81, 0xF6, 0x04, 0xEF, 0x4C, 0x3C, 0x22, 0x53, 0xC0, 0x3D, 0x36, 0xCE } }, // WOW-33978patch9.0.1_Beta { 0x7810113EF44E92B2ULL, { 0xA2, 0x18, 0x6B, 0x58, 0x6A, 0xE6, 0x86, 0xF3, 0xE1, 0x57, 0x9F, 0xFA, 0x9D, 0xDF, 0x17, 0x03 } }, // WOW-33978patch9.0.1_Beta { 0x78B7C378F1424152ULL, { 0xDF, 0x20, 0xB3, 0xFA, 0x41, 0x5B, 0x16, 0x25, 0xAA, 0x7D, 0x82, 0x22, 0x61, 0x2F, 0x75, 0xA0 } }, // WOW-33978patch9.0.1_Beta { 0x79383B3295AA93C4ULL, { 0xEF, 0xDE, 0x6A, 0xEB, 0xDA, 0x6A, 0xBE, 0xD6, 0x5B, 0x2F, 0xE9, 0x1F, 0x33, 0x95, 0xDA, 0x4E } }, // WOW-33978patch9.0.1_Beta { 0x7AD23E997C77CBEBULL, { 0x05, 0x6F, 0x35, 0x4E, 0xA0, 0x86, 0x41, 0x26, 0x53, 0x44, 0x33, 0x6C, 0xEF, 0x0C, 0xDB, 0x8D } }, // WOW-33978patch9.0.1_Beta { 0x7C86AE10DE9A2D24ULL, { 0x7B, 0x0A, 0x70, 0xD0, 0xD1, 0x6E, 0xA7, 0x34, 0x5B, 0xFF, 0xDD, 0x56, 0xF6, 0x5E, 0xA2, 0x7F } }, // WOW-33978patch9.0.1_Beta { 0x7CD6AC9BD4B6C2F1ULL, { 0x20, 0xE7, 0x93, 0x06, 0x6B, 0x7D, 0x48, 0x94, 0x6B, 0xCE, 0x64, 0xA1, 0x6E, 0x72, 0x31, 0xD7 } }, // WOW-33978patch9.0.1_Beta { 0x7E1F1D367C75D4E3ULL, { 0x37, 0x15, 0xB8, 0x5F, 0x6E, 0xCD, 0xBD, 0x3B, 0x1D, 0x85, 0x89, 0x60, 0x22, 0x53, 0xDC, 0x75 } }, // WOW-33978patch9.0.1_Beta { 0x7E4C540FC51875ECULL, { 0x36, 0x71, 0xFC, 0xCE, 0xDF, 0xF2, 0x7D, 0x9F, 0x46, 0x3F, 0x6A, 0x5B, 0xED, 0xE5, 0xE2, 0x2D } }, // WOW-33978patch9.0.1_Beta { 0x7E766271DF1A2F90ULL, { 0x2C, 0xEC, 0x2B, 0x76, 0xCF, 0x05, 0x63, 0x4D, 0xFB, 0xC4, 0xCB, 0x1F, 0xB9, 0x8C, 0xBC, 0x4F } }, // WOW-33978patch9.0.1_Beta { 0x7F7D6EDEF8F5BCFCULL, { 0xF1, 0xD5, 0x9F, 0xEC, 0x5C, 0xB7, 0x4B, 0x2E, 0x66, 0x38, 0x00, 0xB7, 0xDA, 0xBF, 0x32, 0x16 } }, // WOW-33978patch9.0.1_Beta { 0x801FD0E0D505C316ULL, { 0x91, 0x30, 0x64, 0x6A, 0xD7, 0x52, 0x44, 0xBB, 0x44, 0x5B, 0x66, 0x88, 0xA5, 0x00, 0x7B, 0x5C } }, // WOW-33978patch9.0.1_Beta { 0x8281CFAEB6AE6182ULL, { 0xC3, 0x25, 0x34, 0xB1, 0x97, 0x5C, 0x71, 0x07, 0xB6, 0x38, 0xD4, 0x77, 0x94, 0xA9, 0xF3, 0xF2 } }, // WOW-33978patch9.0.1_Beta { 0x834E863E91A946E0ULL, { 0xFF, 0xC4, 0x7E, 0x87, 0xFA, 0xD8, 0x8A, 0x87, 0x24, 0xE2, 0xCF, 0x3E, 0xAE, 0xE1, 0xAA, 0x06 } }, // WOW-33978patch9.0.1_Beta { 0x85B1F0C5AFA7684AULL, { 0x7A, 0x29, 0xDC, 0xA9, 0x90, 0xAA, 0x0A, 0x7D, 0xF1, 0x99, 0x4E, 0xAF, 0x00, 0x04, 0x54, 0xB8 } }, // WOW-33978patch9.0.1_Beta { 0x85CB9DF9FC580C0EULL, { 0xD1, 0x70, 0xBC, 0xFC, 0x13, 0xB1, 0xA2, 0x18, 0x5C, 0x4F, 0xE1, 0x3C, 0x5F, 0x55, 0x5E, 0x2D } }, // WOW-33978patch9.0.1_Beta { 0x867D98DC263C7AE1ULL, { 0xD6, 0xC5, 0xAE, 0x41, 0x33, 0xA0, 0xD3, 0x2F, 0x6A, 0x33, 0x39, 0x19, 0xC9, 0x95, 0xC2, 0x9F } }, // WOW-33978patch9.0.1_Beta { 0x86ACAF9277898823ULL, { 0xE6, 0x3D, 0xFD, 0x69, 0x16, 0xBD, 0xDE, 0x96, 0x32, 0x63, 0x3B, 0x25, 0x5B, 0xEE, 0x73, 0x10 } }, // WOW-33978patch9.0.1_Beta { 0x87413DE10A58AB14ULL, { 0x0B, 0x08, 0x32, 0x50, 0x17, 0x72, 0x9F, 0xD1, 0xBF, 0x2B, 0xA6, 0xC9, 0xEC, 0x03, 0xB0, 0x91 } }, // WOW-33978patch9.0.1_Beta { 0x8831955158E50488ULL, { 0x12, 0x5E, 0x99, 0x2B, 0x50, 0xDD, 0x3C, 0x1D, 0x9B, 0xD5, 0xA4, 0x7D, 0xCA, 0xD7, 0x4F, 0x63 } }, // WOW-33978patch9.0.1_Beta { 0x8A34D884566690F3ULL, { 0xD7, 0x7E, 0x4B, 0x5D, 0x2D, 0x5F, 0x2A, 0x07, 0xD1, 0xB7, 0x4C, 0x77, 0x2C, 0x7B, 0x9E, 0x1D } }, // WOW-33978patch9.0.1_Beta { 0x8AA33E951B061C6CULL, { 0x45, 0xF8, 0xA8, 0x73, 0x94, 0x61, 0xA1, 0x5B, 0x5E, 0xEC, 0xDB, 0x8E, 0xD6, 0xD6, 0x68, 0x20 } }, // WOW-33978patch9.0.1_Beta { 0x8AB2DCD201956FF5ULL, { 0xF4, 0x91, 0x31, 0x04, 0x7B, 0x25, 0xA7, 0xD3, 0x00, 0x67, 0xC7, 0xCB, 0x31, 0xF4, 0xB7, 0xC8 } }, // WOW-33978patch9.0.1_Beta { 0x8AFF128B2C44402FULL, { 0x09, 0x28, 0x59, 0x7D, 0xDA, 0x06, 0x1C, 0xCA, 0x27, 0x72, 0x63, 0x9B, 0x18, 0x15, 0x3B, 0xBA } }, // WOW-33978patch9.0.1_Beta { 0x929399E254952F89ULL, { 0x76, 0x23, 0x36, 0xB5, 0x0C, 0xBB, 0x8C, 0xBF, 0xA6, 0xEC, 0x6F, 0xF2, 0x97, 0x00, 0x96, 0x40 } }, // WOW-33978patch9.0.1_Beta { 0x93A98F82D5B5FA3DULL, { 0x9A, 0x84, 0x56, 0x3A, 0xC7, 0xF1, 0x9E, 0x66, 0xAC, 0x4F, 0x63, 0x34, 0x6D, 0x58, 0xCB, 0xBC } }, // WOW-33978patch9.0.1_Beta { 0x9440B38F8FC83801ULL, { 0xF2, 0x35, 0xB5, 0x29, 0x24, 0x61, 0xAD, 0xD2, 0xCD, 0x58, 0x27, 0x5F, 0xDA, 0xBF, 0x2D, 0x91 } }, // WOW-33978patch9.0.1_Beta { 0x953B60DFAAD53C53ULL, { 0x0F, 0xFD, 0xE3, 0x48, 0xDB, 0xBF, 0x68, 0x28, 0x10, 0x38, 0x61, 0xE7, 0xC2, 0x02, 0xEE, 0x03 } }, // WOW-33978patch9.0.1_Beta { 0x96CDB729EF5FF4D8ULL, { 0x9D, 0x5C, 0xD0, 0x16, 0x7D, 0xA6, 0x0B, 0x4A, 0x5D, 0x5B, 0x7E, 0xB1, 0x0F, 0x1F, 0x44, 0x02 } }, // WOW-33978patch9.0.1_Beta { 0x96D55B6111ADB046ULL, { 0x7D, 0x8B, 0x9D, 0x2B, 0x57, 0x88, 0xCF, 0x14, 0x94, 0xE1, 0xBE, 0x73, 0xA3, 0x2A, 0x0A, 0xEB } }, // WOW-33978patch9.0.1_Beta { 0x994C40C879819D41ULL, { 0x88, 0xA3, 0x28, 0x44, 0xB2, 0x2E, 0xB1, 0xE8, 0x36, 0x2B, 0x72, 0x8A, 0xE5, 0x1F, 0x66, 0x63 } }, // WOW-33978patch9.0.1_Beta { 0x9A4F5BC0D2DF3E7CULL, { 0x27, 0x5D, 0x03, 0x60, 0xE8, 0x11, 0xEE, 0x2F, 0x3E, 0xB3, 0xBC, 0xCA, 0x96, 0xFA, 0x39, 0x88 } }, // WOW-33978patch9.0.1_Beta { 0x9BB64D7C24F7F570ULL, { 0xA5, 0xF5, 0x27, 0xB1, 0x2A, 0x7C, 0x54, 0x49, 0x01, 0x30, 0x1F, 0x0C, 0xC4, 0xF7, 0x5A, 0xC6 } }, // WOW-33978patch9.0.1_Beta { 0x9CAE05184985E6FAULL, { 0x7D, 0x79, 0xCA, 0x06, 0x09, 0xB2, 0xA9, 0xF5, 0x2C, 0x17, 0xB4, 0x91, 0xB2, 0x2F, 0xF2, 0x95 } }, // WOW-33978patch9.0.1_Beta { 0x9DE3C22E44D3D817ULL, { 0x2A, 0xD1, 0x8A, 0xE6, 0x65, 0x74, 0xB1, 0x78, 0x4A, 0x9B, 0x52, 0xC4, 0xE4, 0x5C, 0x47, 0x0C } }, // WOW-33978patch9.0.1_Beta { 0x9E10A1C6E2156E96ULL, { 0xA1, 0x4F, 0x9D, 0x5B, 0x4B, 0x8E, 0x6F, 0xFC, 0x17, 0x33, 0x48, 0xA8, 0x16, 0x89, 0x88, 0x2D } }, // WOW-33978patch9.0.1_Beta { 0x9E84167C7BCCAECFULL, { 0xBB, 0x28, 0xC7, 0x41, 0x89, 0x32, 0x7B, 0xEC, 0x53, 0xC1, 0xF1, 0xD9, 0x0D, 0x62, 0xBD, 0xA6 } }, // WOW-33978patch9.0.1_Beta { 0xA1651FBBB33533C8ULL, { 0xAB, 0x1E, 0x3D, 0x12, 0x8C, 0x6E, 0xC9, 0x4E, 0x6C, 0x7E, 0x0B, 0xAB, 0x3F, 0xF9, 0x6B, 0x54 } }, // WOW-33978patch9.0.1_Beta { 0xA1F082630668B975ULL, { 0xA0, 0xC9, 0xAB, 0x98, 0x7F, 0x54, 0x4D, 0xAD, 0x86, 0x73, 0x5E, 0xEC, 0x3A, 0xB7, 0x0C, 0xBC } }, // WOW-33978patch9.0.1_Beta { 0xA2A3787CEE9A05EEULL, { 0x2F, 0xAA, 0x35, 0x7F, 0x63, 0xC7, 0x2E, 0x74, 0x9B, 0x42, 0x3E, 0x45, 0xA7, 0x21, 0xE5, 0x49 } }, // WOW-33978patch9.0.1_Beta { 0xA6D012ADD54A30CEULL, { 0x22, 0xE1, 0x30, 0x2E, 0xEB, 0xD9, 0x98, 0xCB, 0x7F, 0x05, 0xBC, 0xAF, 0x1D, 0x85, 0x39, 0xC6 } }, // WOW-33978patch9.0.1_Beta { 0xA7E2CEFD0280FD3AULL, { 0x08, 0x90, 0x89, 0x66, 0x8F, 0xD4, 0xF2, 0xCA, 0x94, 0xE0, 0x28, 0xCE, 0x0E, 0x8F, 0x0E, 0xAD } }, // WOW-33978patch9.0.1_Beta { 0xA8E1094D80B82D21ULL, { 0x8F, 0xA8, 0xB2, 0xEE, 0x12, 0xF4, 0x5E, 0xD0, 0xF2, 0x40, 0x1C, 0x03, 0x3B, 0x32, 0x43, 0x8C } }, // WOW-33978patch9.0.1_Beta { 0xAA15EAD581169812ULL, { 0x60, 0xAA, 0x2D, 0x1C, 0xB7, 0xC2, 0x92, 0x84, 0xDD, 0x63, 0x6E, 0x35, 0x77, 0xAF, 0x3E, 0xFD } }, // WOW-33978patch9.0.1_Beta { 0xAA3EB38B572073F9ULL, { 0xE5, 0xBC, 0x7A, 0x03, 0x54, 0xB6, 0x45, 0x39, 0xCA, 0x59, 0xB6, 0x78, 0x70, 0x3C, 0xE4, 0x72 } }, // WOW-33978patch9.0.1_Beta { 0xAA7119519C968451ULL, { 0x57, 0x84, 0xAC, 0xD6, 0x7A, 0xAD, 0xFF, 0xA5, 0xA3, 0x50, 0x84, 0xC8, 0xB7, 0x97, 0x4B, 0x7E } }, // WOW-33978patch9.0.1_Beta { 0xAAAD5C17B34A935FULL, { 0xB0, 0x95, 0x4F, 0x6A, 0x29, 0x14, 0x19, 0x10, 0x81, 0x00, 0x93, 0x49, 0xAF, 0x44, 0x43, 0xC3 } }, // WOW-33978patch9.0.1_Beta { 0xABA6C1AFDB427F54ULL, { 0x29, 0x75, 0xC6, 0x9D, 0xA6, 0x14, 0xC3, 0xFF, 0x32, 0xEE, 0xC3, 0xFE, 0xD8, 0x47, 0x29, 0xDE } }, // WOW-33978patch9.0.1_Beta { 0xADC58189215E1C48ULL, { 0x1F, 0xFB, 0xD3, 0xB8, 0xE6, 0x2A, 0x10, 0x24, 0x92, 0xDC, 0x30, 0x73, 0x21, 0x16, 0xF1, 0x62 } }, // WOW-33978patch9.0.1_Beta { 0xAFEE51897035872EULL, { 0xF0, 0x85, 0xEA, 0xBC, 0x11, 0xE2, 0x92, 0xD2, 0x0C, 0x77, 0x8B, 0x6D, 0x89, 0x2A, 0xBD, 0x1E } }, // WOW-33978patch9.0.1_Beta { 0xAFFFA5791F73520AULL, { 0xA9, 0x67, 0xF2, 0xF4, 0x7E, 0x7C, 0xC0, 0x05, 0xCB, 0x8D, 0x7E, 0x92, 0x84, 0x78, 0x15, 0x8E } }, // WOW-33978patch9.0.1_Beta { 0xB032F43502EDFA5AULL, { 0x4B, 0x0E, 0xF7, 0x9E, 0x47, 0x2D, 0x00, 0x2F, 0x8E, 0x71, 0x7E, 0xED, 0x75, 0xB0, 0x3E, 0x69 } }, // WOW-33978patch9.0.1_Beta { 0xB23472311441BF43ULL, { 0x3E, 0xE4, 0x12, 0x48, 0xB9, 0x9B, 0xF6, 0xA1, 0x7E, 0xF9, 0x63, 0xC2, 0xD6, 0x88, 0x7B, 0xF5 } }, // WOW-33978patch9.0.1_Beta { 0xB4582D7E642035F5ULL, { 0xCA, 0x5B, 0x90, 0xA2, 0x8C, 0x7D, 0xAE, 0xF5, 0xAC, 0x8B, 0xA2, 0x6A, 0x3B, 0x7E, 0xB2, 0x6F } }, // WOW-33978patch9.0.1_Beta { 0xB46B23A8B047E71AULL, { 0x5F, 0xD3, 0xB3, 0xE6, 0xFF, 0x1B, 0xEB, 0x79, 0x6B, 0x13, 0xA0, 0x44, 0x7C, 0x8C, 0x9E, 0xCE } }, // WOW-33978patch9.0.1_Beta { 0xB62985BE1B0E820CULL, { 0x7B, 0xCB, 0x7A, 0x3A, 0x7A, 0x7F, 0xE6, 0x72, 0xD7, 0xBB, 0x8D, 0xAB, 0x4C, 0x32, 0x66, 0xCA } }, // WOW-33978patch9.0.1_Beta { 0xB76D3CD9FA80EF23ULL, { 0x43, 0x2F, 0xDE, 0xF3, 0x70, 0xE6, 0x84, 0x73, 0xCE, 0xB7, 0x5B, 0xE4, 0x80, 0x28, 0x71, 0x49 } }, // WOW-33978patch9.0.1_Beta { 0xB881ACD9C433C39BULL, { 0x41, 0x5B, 0xEC, 0x80, 0x13, 0x4B, 0x91, 0xEB, 0xA7, 0xA8, 0x94, 0xE8, 0x11, 0x1F, 0x16, 0xED } }, // WOW-33978patch9.0.1_Beta { 0xB8F711CD1BCCCB5AULL, { 0x06, 0x8D, 0xA8, 0x49, 0x5F, 0x4F, 0x87, 0x05, 0x08, 0xDF, 0xAF, 0xCE, 0xFA, 0xB3, 0x4F, 0x6C } }, // WOW-33978patch9.0.1_Beta { 0xBA2586D5321809C9ULL, { 0x37, 0x08, 0xFF, 0xBC, 0xD5, 0xDE, 0x8B, 0xA1, 0x1E, 0x7A, 0xE2, 0xF2, 0x10, 0x88, 0xF5, 0xE0 } }, // WOW-33978patch9.0.1_Beta { 0xBB56A0BAE42B0A58ULL, { 0x36, 0xF6, 0x57, 0xC6, 0x27, 0x16, 0xEA, 0x72, 0xEB, 0x46, 0x44, 0x6C, 0xD3, 0x6E, 0xF0, 0x81 } }, // WOW-33978patch9.0.1_Beta { 0xBBC9FAFBFB8A476EULL, { 0xDF, 0x8F, 0x45, 0x6C, 0xE6, 0xF4, 0x2B, 0x83, 0x29, 0xD6, 0xAB, 0x75, 0x44, 0x5E, 0x93, 0x47 } }, // WOW-33978patch9.0.1_Beta { 0xBDE676123BBB010AULL, { 0x9A, 0x68, 0x26, 0xB6, 0x35, 0x2B, 0xF4, 0x47, 0xC5, 0x52, 0x6F, 0x3D, 0x80, 0xB2, 0xA4, 0x2A } }, // WOW-33978patch9.0.1_Beta { 0xBE78B15D2D2FFC28ULL, { 0x18, 0x63, 0x5E, 0xA1, 0xEA, 0x0D, 0x52, 0xA8, 0x04, 0x34, 0x5D, 0x74, 0x2E, 0x47, 0xE7, 0xFF } }, // WOW-33978patch9.0.1_Beta { 0xBF0DA6AAE66E25A0ULL, { 0xFB, 0x2D, 0xEA, 0x41, 0xD6, 0x00, 0xF3, 0x95, 0xB4, 0x1F, 0x1D, 0x21, 0x9C, 0x2F, 0xDE, 0x4D } }, // WOW-33978patch9.0.1_Beta { 0xBF26C727BFC92EAFULL, { 0x5E, 0xD5, 0x02, 0xEE, 0xFB, 0x92, 0x1A, 0x27, 0xC1, 0x83, 0x6A, 0x8E, 0x5E, 0x4A, 0x15, 0x92 } }, // WOW-33978patch9.0.1_Beta { 0xBF874CF500A50632ULL, { 0x87, 0xB9, 0x5B, 0xE0, 0x9C, 0xEE, 0x36, 0xDC, 0x91, 0x43, 0x04, 0x70, 0x95, 0x2E, 0xF3, 0x26 } }, // WOW-33978patch9.0.1_Beta { 0xBF87DE9EFBA3E7E7ULL, { 0x2A, 0x2C, 0x1E, 0x9A, 0xCA, 0x4F, 0xCD, 0x01, 0xF8, 0xF6, 0x40, 0x66, 0x76, 0x70, 0x63, 0x79 } }, // WOW-33978patch9.0.1_Beta { 0xC132034A5F2F7E10ULL, { 0x27, 0x3B, 0xA2, 0x00, 0x5D, 0x7C, 0xDE, 0xDD, 0xB1, 0x67, 0x5C, 0x8C, 0x0F, 0x74, 0x1B, 0x7A } }, // WOW-33978patch9.0.1_Beta { 0xC16F60A6BAD360C0ULL, { 0xBB, 0x8E, 0xF0, 0x39, 0x32, 0xAC, 0x38, 0xC6, 0xCE, 0x11, 0xFE, 0x67, 0x96, 0xEB, 0xBD, 0xEC } }, // WOW-33978patch9.0.1_Beta { 0xC2BCD68E7A2AE4AFULL, { 0x89, 0x48, 0x11, 0x50, 0x8B, 0x28, 0xE9, 0x0B, 0xFA, 0xB0, 0x28, 0x4D, 0xA2, 0x3E, 0xE5, 0x06 } }, // WOW-33978patch9.0.1_Beta { 0xC312DAF915B31117ULL, { 0xDA, 0xB0, 0x06, 0x6B, 0x4F, 0x8C, 0xD0, 0x79, 0x54, 0x8C, 0xE7, 0x52, 0x88, 0x4E, 0x19, 0x49 } }, // WOW-33978patch9.0.1_Beta { 0xC63B911BD06869D3ULL, { 0xCB, 0x82, 0xD1, 0x56, 0x11, 0xA0, 0x96, 0xAA, 0xD6, 0xB2, 0xD9, 0x48, 0x8A, 0x87, 0x93, 0x9A } }, // WOW-33978patch9.0.1_Beta { 0xC65443D5AB354639ULL, { 0xA9, 0x85, 0x72, 0x55, 0xBB, 0x6E, 0x5A, 0x75, 0xFA, 0x9A, 0xFE, 0xC8, 0xDA, 0xC2, 0xB0, 0x7A } }, // WOW-33978patch9.0.1_Beta { 0xC8167C2D20426AD8ULL, { 0xF4, 0xF7, 0x7B, 0x2C, 0xA8, 0x36, 0xA4, 0xF9, 0xBE, 0x53, 0x61, 0x6E, 0xC6, 0x2D, 0x61, 0x84 } }, // WOW-33978patch9.0.1_Beta { 0xC88BB3653C3C964FULL, { 0x7B, 0x61, 0xCA, 0x6E, 0x9C, 0x98, 0x29, 0xF2, 0xF4, 0x52, 0xD2, 0x17, 0x37, 0xD9, 0xD7, 0x23 } }, // WOW-33978patch9.0.1_Beta { 0xC926C2CBE0DFF9BEULL, { 0x9D, 0x29, 0x47, 0x37, 0xAB, 0x13, 0xC3, 0x6B, 0x02, 0xEC, 0x0E, 0x0B, 0x77, 0x65, 0x7B, 0x13 } }, // WOW-33978patch9.0.1_Beta { 0xC9A159E7B047AE82ULL, { 0xE4, 0x3C, 0x35, 0x49, 0xE6, 0x0E, 0xB1, 0x2C, 0x49, 0xF9, 0xE9, 0x57, 0x38, 0x61, 0xA5, 0xE0 } }, // WOW-33978patch9.0.1_Beta { 0xC9F63551DC0A7DD3ULL, { 0xAA, 0xD6, 0xE9, 0xB5, 0x71, 0x97, 0x1E, 0x0A, 0xE9, 0x21, 0x22, 0xE9, 0x77, 0x28, 0x96, 0xDC } }, // WOW-33978patch9.0.1_Beta { 0xC9FDAB91F9DABA18ULL, { 0x02, 0x5D, 0xF3, 0x9D, 0x8B, 0xF6, 0x32, 0xE6, 0x8B, 0x0A, 0x4E, 0x74, 0xC1, 0x44, 0x2A, 0x24 } }, // WOW-33978patch9.0.1_Beta { 0xCA9C5B912FFBCAC7ULL, { 0xAF, 0xEE, 0xA5, 0xF1, 0x64, 0xC4, 0x0C, 0xF3, 0x84, 0x98, 0xD8, 0xBC, 0x5A, 0x0C, 0x6B, 0x8F } }, // WOW-33978patch9.0.1_Beta { 0xCB4D2A3F81E1794EULL, { 0x12, 0x0C, 0x6B, 0x4A, 0x25, 0x41, 0xB5, 0xA9, 0x79, 0x81, 0x0C, 0x35, 0x38, 0x8C, 0x56, 0xF1 } }, // WOW-33978patch9.0.1_Beta { 0xCBBACF102B89411FULL, { 0xE0, 0x4C, 0x71, 0x77, 0x15, 0xB0, 0x06, 0x11, 0xAC, 0x7E, 0xA9, 0x7D, 0x0B, 0x45, 0xAA, 0x0C } }, // WOW-33978patch9.0.1_Beta { 0xD0C5486A7AD05CF9ULL, { 0x54, 0x84, 0xBE, 0x9C, 0xB3, 0x7D, 0xE9, 0x51, 0x12, 0xA1, 0x8B, 0x60, 0x52, 0xC5, 0xA7, 0xE5 } }, // WOW-33978patch9.0.1_Beta { 0xD362C01C25B71B50ULL, { 0x08, 0xFB, 0x22, 0xF6, 0xA9, 0x52, 0x2A, 0x48, 0xCA, 0x24, 0x79, 0xCD, 0xED, 0x66, 0xA1, 0x75 } }, // WOW-33978patch9.0.1_Beta { 0xD3CD986A5533AE96ULL, { 0x56, 0x70, 0x8F, 0x30, 0x77, 0xB7, 0x24, 0x88, 0x10, 0xC7, 0xB8, 0x7E, 0xDA, 0xCF, 0x25, 0xC2 } }, // WOW-33978patch9.0.1_Beta { 0xD628DFD6FD4C4629ULL, { 0x30, 0x0F, 0x61, 0x98, 0xF8, 0xD3, 0x4F, 0x71, 0x81, 0x11, 0x61, 0x52, 0x13, 0x31, 0x4E, 0x07 } }, // WOW-33978patch9.0.1_Beta { 0xD843A7FFCE96C7AEULL, { 0x08, 0xF1, 0xDE, 0x2D, 0x96, 0xEB, 0x99, 0x9C, 0x89, 0xA1, 0xD4, 0xE3, 0xBD, 0x22, 0x34, 0x3D } }, // WOW-33978patch9.0.1_Beta { 0xD8EDA6A4C2799AFCULL, { 0x82, 0xD9, 0x7B, 0x8F, 0x98, 0x6A, 0xF1, 0x57, 0x66, 0xC8, 0x9F, 0x61, 0x1C, 0x14, 0x68, 0x88 } }, // WOW-33978patch9.0.1_Beta { 0xDB37969AE172E0A2ULL, { 0xE0, 0x71, 0xBF, 0xF3, 0x31, 0x7D, 0x6F, 0xCC, 0x61, 0x12, 0xE8, 0xCB, 0xF0, 0x4B, 0x9E, 0x84 } }, // WOW-33978patch9.0.1_Beta { 0xDBD4F370AE374627ULL, { 0x0D, 0x78, 0x08, 0xF6, 0x29, 0xBE, 0x2E, 0x0B, 0xE2, 0x80, 0x73, 0x1F, 0x9C, 0xCD, 0x51, 0xAE } }, // WOW-33978patch9.0.1_Beta { 0xDC692CF534CF6094ULL, { 0xA6, 0x41, 0xD3, 0x32, 0xCE, 0x02, 0xCF, 0x14, 0x71, 0x42, 0x9F, 0xA3, 0x5B, 0x9C, 0xC2, 0x2A } }, // WOW-33978patch9.0.1_Beta { 0xDE7D8C810DEBCCBBULL, { 0x39, 0xBD, 0x23, 0xF7, 0xBA, 0x29, 0x73, 0xCF, 0x57, 0x36, 0xE8, 0xA4, 0xD1, 0x37, 0xA2, 0x59 } }, // WOW-33978patch9.0.1_Beta { 0xDFDBE74EFD0C1D9AULL, { 0x80, 0xA6, 0x40, 0x5D, 0xCA, 0x4A, 0xDB, 0x58, 0x04, 0xDF, 0xF3, 0x9E, 0x25, 0xAA, 0x6A, 0xF0 } }, // WOW-33978patch9.0.1_Beta { 0xE186FB75B0C94A7CULL, { 0x9F, 0x7B, 0x27, 0xC6, 0xD8, 0x4C, 0x80, 0x59, 0xEF, 0xD8, 0x81, 0x56, 0x0C, 0x84, 0xFB, 0xF2 } }, // WOW-33978patch9.0.1_Beta { 0xE26933D3B03457B8ULL, { 0xE4, 0xE9, 0x79, 0x3A, 0x4B, 0x0F, 0xFA, 0x09, 0xC6, 0x9A, 0x2F, 0xB3, 0xC4, 0x86, 0xE4, 0x5C } }, // WOW-33978patch9.0.1_Beta { 0xE2D4CD545D19C5B3ULL, { 0x5F, 0x4D, 0xC6, 0x65, 0x17, 0xB5, 0x26, 0xCF, 0x48, 0x18, 0xDD, 0xDA, 0x50, 0xAE, 0xD6, 0x2A } }, // WOW-33978patch9.0.1_Beta { 0xE321D1E0F5FD72D1ULL, { 0x39, 0x7A, 0xB9, 0x76, 0xC9, 0xFF, 0x03, 0x78, 0x37, 0xF9, 0x26, 0x00, 0xBE, 0x75, 0x4C, 0x63 } }, // WOW-33978patch9.0.1_Beta { 0xE50F70F06A579FCCULL, { 0xE2, 0xB1, 0x53, 0x8D, 0x2C, 0x14, 0xAD, 0x00, 0xC9, 0xF2, 0xB1, 0xFE, 0x56, 0xC1, 0xF0, 0xD2 } }, // WOW-33978patch9.0.1_Beta { 0xE6DE529515D5322EULL, { 0x1D, 0x58, 0x84, 0x6A, 0x1C, 0x87, 0x1B, 0xA0, 0x8C, 0x2E, 0xCD, 0x7B, 0x32, 0xFA, 0xCA, 0x41 } }, // WOW-33978patch9.0.1_Beta { 0xE6F58B08B45D7A12ULL, { 0x18, 0xF3, 0x4D, 0xA5, 0xA6, 0x91, 0xF6, 0xAA, 0xF1, 0xF9, 0xA7, 0x05, 0xB6, 0x56, 0xE0, 0x1F } }, // WOW-33978patch9.0.1_Beta { 0xE7357F7B88B8338DULL, { 0x9B, 0xAC, 0x5D, 0x21, 0x9C, 0xE7, 0x19, 0x84, 0xCB, 0xD6, 0xBE, 0x98, 0xE7, 0x2A, 0xB8, 0x3C } }, // WOW-33978patch9.0.1_Beta { 0xE8F494F7E371D157ULL, { 0x4E, 0x2C, 0xDA, 0xA4, 0x18, 0x44, 0x03, 0x48, 0xE4, 0x39, 0x99, 0x5D, 0x64, 0x28, 0x4B, 0x73 } }, // WOW-33978patch9.0.1_Beta { 0xE995E22AF7111D57ULL, { 0x33, 0xE8, 0xD6, 0x3D, 0xF4, 0xA8, 0xCE, 0x36, 0xD1, 0x88, 0xEC, 0x07, 0x22, 0x09, 0xD1, 0x84 } }, // WOW-33978patch9.0.1_Beta { 0xEA9589F00A338035ULL, { 0xEC, 0xCB, 0x9C, 0xA5, 0x7C, 0xA0, 0x30, 0x33, 0x9B, 0x2D, 0xBA, 0x74, 0x5B, 0x5C, 0x14, 0x9B } }, // WOW-33978patch9.0.1_Beta { 0xEAC344DACB040496ULL, { 0x51, 0x22, 0xD6, 0x18, 0x1F, 0x79, 0x71, 0x9A, 0x81, 0x7B, 0xBF, 0x84, 0xA2, 0xB5, 0x7B, 0xA1 } }, // WOW-33978patch9.0.1_Beta { 0xEB5B9AA992D0582BULL, { 0x3A, 0xC4, 0xA8, 0x0F, 0x38, 0x66, 0x3F, 0xB3, 0xAD, 0xEB, 0x1C, 0xE5, 0x5E, 0xD3, 0xC1, 0x4D } }, // WOW-33978patch9.0.1_Beta { 0xEB824892D843D365ULL, { 0x39, 0x4F, 0xCE, 0x26, 0x76, 0x68, 0x05, 0xA7, 0x68, 0x14, 0xB2, 0x5B, 0x4B, 0x72, 0xB4, 0xD8 } }, // WOW-33978patch9.0.1_Beta { 0xEC434CD731FF1D6EULL, { 0x84, 0xC4, 0x3B, 0x20, 0xA6, 0x33, 0x66, 0x82, 0xB5, 0xAA, 0x1D, 0x7C, 0xFA, 0x00, 0xDA, 0xE3 } }, // WOW-33978patch9.0.1_Beta { 0xED4548400C6CD9B3ULL, { 0xDD, 0xAE, 0x53, 0x8A, 0x39, 0x59, 0x16, 0x11, 0xBF, 0x1D, 0x2E, 0xEE, 0x97, 0x34, 0xF8, 0xDD } }, // WOW-33978patch9.0.1_Beta { 0xED85FA32ABE4D31AULL, { 0xC9, 0x35, 0xBB, 0xCB, 0x24, 0x70, 0xF2, 0x97, 0xEA, 0x61, 0xF8, 0x66, 0x9D, 0xE7, 0x38, 0x10 } }, // WOW-33978patch9.0.1_Beta { 0xEDB14BC9682C3EC7ULL, { 0x07, 0x63, 0x2A, 0xA3, 0x73, 0xD5, 0x08, 0xD9, 0x94, 0x81, 0x64, 0x6E, 0xF5, 0x2C, 0x6F, 0x21 } }, // WOW-33978patch9.0.1_Beta { 0xEFD8CF7039C9E3ABULL, { 0x6B, 0xA7, 0x1A, 0xEC, 0x7C, 0xF7, 0xED, 0x83, 0xE7, 0xD4, 0x7A, 0x47, 0xE1, 0xAA, 0x97, 0x58 } }, // WOW-33978patch9.0.1_Beta { 0xEFF36C5D384458ADULL, { 0x9F, 0x0B, 0x12, 0x0C, 0x9D, 0x86, 0xB5, 0x9B, 0x2C, 0x8B, 0x5E, 0xB0, 0x6C, 0xE0, 0x33, 0xC2 } }, // WOW-33978patch9.0.1_Beta { 0xF04AF4C9E8BD1063ULL, { 0x62, 0xDB, 0x73, 0x69, 0x77, 0x96, 0x60, 0xE4, 0xAC, 0x83, 0xB1, 0xAF, 0x77, 0xCD, 0x86, 0x0A } }, // WOW-33978patch9.0.1_Beta { 0xF0C135866D740895ULL, { 0x44, 0xAD, 0xC9, 0xAC, 0xBE, 0xD5, 0x0F, 0x49, 0x0A, 0xD8, 0xD5, 0xEE, 0xA8, 0x13, 0xDB, 0x69 } }, // WOW-33978patch9.0.1_Beta { 0xF399E2091BB4EB3DULL, { 0xC3, 0x8C, 0x68, 0x01, 0x59, 0xE6, 0x3F, 0x03, 0x65, 0xFF, 0x10, 0xC6, 0xB4, 0x8C, 0xF6, 0xBC } }, // WOW-33978patch9.0.1_Beta { 0xF4941ADAEFD62B50ULL, { 0x4E, 0x37, 0xCC, 0x48, 0xF5, 0x1E, 0x3B, 0x5F, 0x73, 0xCC, 0xD7, 0x41, 0x90, 0x91, 0xE3, 0xB6 } }, // WOW-33978patch9.0.1_Beta { 0xF4A0D7C6BE16E9E6ULL, { 0xB9, 0x6B, 0x07, 0x4E, 0x60, 0xBB, 0x6B, 0x0C, 0x3F, 0xFD, 0x1F, 0xBC, 0x87, 0x77, 0x36, 0x7C } }, // WOW-33978patch9.0.1_Beta { 0xF5BC79A1093D4297ULL, { 0xE1, 0x18, 0x0A, 0x55, 0xA6, 0x7B, 0x29, 0xAC, 0xDA, 0x9B, 0x6A, 0x3A, 0x70, 0x4C, 0x90, 0x27 } }, // WOW-33978patch9.0.1_Beta { 0xF88CD79C16ED40C1ULL, { 0xED, 0x5B, 0xC5, 0xBF, 0x5D, 0x1E, 0xD1, 0x69, 0x51, 0x9F, 0x85, 0x9F, 0x4C, 0xEB, 0xAB, 0xA9 } }, // WOW-33978patch9.0.1_Beta { 0xF9B971BF3BAE0F5FULL, { 0x41, 0x6A, 0x38, 0x03, 0x5B, 0xAD, 0xD4, 0xC2, 0x7A, 0x14, 0xF1, 0xE8, 0x74, 0xB1, 0x0F, 0xFC } }, // WOW-33978patch9.0.1_Beta { 0xFA6C85CFB99D0738ULL, { 0x41, 0xFA, 0x48, 0x6B, 0x2F, 0xEB, 0x68, 0xFB, 0x96, 0x64, 0xD4, 0x22, 0xAA, 0xF1, 0x71, 0x38 } }, // WOW-33978patch9.0.1_Beta { 0xFB90D71D100E9595ULL, { 0xB7, 0xA7, 0x6E, 0x35, 0x33, 0x75, 0x11, 0xB4, 0xA2, 0x6E, 0xE9, 0xBE, 0x0E, 0xCA, 0xD3, 0xD0 } }, // WOW-33978patch9.0.1_Beta { 0xFD06442092131C5EULL, { 0x3E, 0x55, 0xE1, 0x47, 0xB6, 0x53, 0xB5, 0xF9, 0xE6, 0x8E, 0xAB, 0x44, 0x90, 0x8F, 0xBD, 0xC1 } }, // WOW-33978patch9.0.1_Beta { 0xFD0BA919048A69AAULL, { 0x66, 0xE4, 0x75, 0xF4, 0x60, 0x26, 0xB0, 0x91, 0xAD, 0xD4, 0xA3, 0xE1, 0xBC, 0x61, 0x96, 0x91 } }, // WOW-33978patch9.0.1_Beta { 0xFF08D3B90FB93B8CULL, { 0xD4, 0x27, 0x97, 0x22, 0xFB, 0x8B, 0xDD, 0x2A, 0xA8, 0xB3, 0xA4, 0xC7, 0x4E, 0xFD, 0x44, 0xCA } }, // WOW-33978patch9.0.1_Beta { 0xFF7C9A1B789D0D42ULL, { 0xA9, 0xEC, 0x27, 0x53, 0x3B, 0x7D, 0x9D, 0xB1, 0xE2, 0x39, 0xEC, 0x68, 0x8B, 0xA6, 0x53, 0xF4 } }, // WOW-33978patch9.0.1_Beta }; //----------------------------------------------------------------------------- // Local functions static void Initialize(PCASC_SALSA20 pState, LPBYTE pbKey, DWORD cbKeyLength, LPBYTE pbVector) { const char * szConstants = (cbKeyLength == 32) ? szKeyConstant32 : szKeyConstant16; DWORD KeyIndex = cbKeyLength - 0x10; memset(pState, 0, sizeof(CASC_SALSA20)); pState->Key[0] = *(PDWORD)(szConstants + 0x00); pState->Key[1] = *(PDWORD)(pbKey + 0x00); pState->Key[2] = *(PDWORD)(pbKey + 0x04); pState->Key[3] = *(PDWORD)(pbKey + 0x08); pState->Key[4] = *(PDWORD)(pbKey + 0x0C); pState->Key[5] = *(PDWORD)(szConstants + 0x04); pState->Key[6] = *(PDWORD)(pbVector + 0x00); pState->Key[7] = *(PDWORD)(pbVector + 0x04); pState->Key[8] = 0; pState->Key[9] = 0; pState->Key[10] = *(PDWORD)(szConstants + 0x08); pState->Key[11] = *(PDWORD)(pbKey + KeyIndex + 0x00); pState->Key[12] = *(PDWORD)(pbKey + KeyIndex + 0x04); pState->Key[13] = *(PDWORD)(pbKey + KeyIndex + 0x08); pState->Key[14] = *(PDWORD)(pbKey + KeyIndex + 0x0C); pState->Key[15] = *(PDWORD)(szConstants + 0x0C); pState->dwRounds = 20; } static int Decrypt(PCASC_SALSA20 pState, LPBYTE pbOutBuffer, LPBYTE pbInBuffer, size_t cbInBuffer) { LPBYTE pbXorValue; DWORD KeyMirror[0x10]; DWORD XorValue[0x10]; DWORD BlockSize; DWORD i; // Repeat until we have data to read while(cbInBuffer > 0) { // Create the copy of the key memcpy(KeyMirror, pState->Key, sizeof(KeyMirror)); // Shuffle the key for(i = 0; i < pState->dwRounds; i += 2) { KeyMirror[0x04] ^= Rol32((KeyMirror[0x00] + KeyMirror[0x0C]), 0x07); KeyMirror[0x08] ^= Rol32((KeyMirror[0x04] + KeyMirror[0x00]), 0x09); KeyMirror[0x0C] ^= Rol32((KeyMirror[0x08] + KeyMirror[0x04]), 0x0D); KeyMirror[0x00] ^= Rol32((KeyMirror[0x0C] + KeyMirror[0x08]), 0x12); KeyMirror[0x09] ^= Rol32((KeyMirror[0x05] + KeyMirror[0x01]), 0x07); KeyMirror[0x0D] ^= Rol32((KeyMirror[0x09] + KeyMirror[0x05]), 0x09); KeyMirror[0x01] ^= Rol32((KeyMirror[0x0D] + KeyMirror[0x09]), 0x0D); KeyMirror[0x05] ^= Rol32((KeyMirror[0x01] + KeyMirror[0x0D]), 0x12); KeyMirror[0x0E] ^= Rol32((KeyMirror[0x0A] + KeyMirror[0x06]), 0x07); KeyMirror[0x02] ^= Rol32((KeyMirror[0x0E] + KeyMirror[0x0A]), 0x09); KeyMirror[0x06] ^= Rol32((KeyMirror[0x02] + KeyMirror[0x0E]), 0x0D); KeyMirror[0x0A] ^= Rol32((KeyMirror[0x06] + KeyMirror[0x02]), 0x12); KeyMirror[0x03] ^= Rol32((KeyMirror[0x0F] + KeyMirror[0x0B]), 0x07); KeyMirror[0x07] ^= Rol32((KeyMirror[0x03] + KeyMirror[0x0F]), 0x09); KeyMirror[0x0B] ^= Rol32((KeyMirror[0x07] + KeyMirror[0x03]), 0x0D); KeyMirror[0x0F] ^= Rol32((KeyMirror[0x0B] + KeyMirror[0x07]), 0x12); KeyMirror[0x01] ^= Rol32((KeyMirror[0x00] + KeyMirror[0x03]), 0x07); KeyMirror[0x02] ^= Rol32((KeyMirror[0x01] + KeyMirror[0x00]), 0x09); KeyMirror[0x03] ^= Rol32((KeyMirror[0x02] + KeyMirror[0x01]), 0x0D); KeyMirror[0x00] ^= Rol32((KeyMirror[0x03] + KeyMirror[0x02]), 0x12); KeyMirror[0x06] ^= Rol32((KeyMirror[0x05] + KeyMirror[0x04]), 0x07); KeyMirror[0x07] ^= Rol32((KeyMirror[0x06] + KeyMirror[0x05]), 0x09); KeyMirror[0x04] ^= Rol32((KeyMirror[0x07] + KeyMirror[0x06]), 0x0D); KeyMirror[0x05] ^= Rol32((KeyMirror[0x04] + KeyMirror[0x07]), 0x12); KeyMirror[0x0B] ^= Rol32((KeyMirror[0x0A] + KeyMirror[0x09]), 0x07); KeyMirror[0x08] ^= Rol32((KeyMirror[0x0B] + KeyMirror[0x0A]), 0x09); KeyMirror[0x09] ^= Rol32((KeyMirror[0x08] + KeyMirror[0x0B]), 0x0D); KeyMirror[0x0A] ^= Rol32((KeyMirror[0x09] + KeyMirror[0x08]), 0x12); KeyMirror[0x0C] ^= Rol32((KeyMirror[0x0F] + KeyMirror[0x0E]), 0x07); KeyMirror[0x0D] ^= Rol32((KeyMirror[0x0C] + KeyMirror[0x0F]), 0x09); KeyMirror[0x0E] ^= Rol32((KeyMirror[0x0D] + KeyMirror[0x0C]), 0x0D); KeyMirror[0x0F] ^= Rol32((KeyMirror[0x0E] + KeyMirror[0x0D]), 0x12); } // Set the number of remaining bytes pbXorValue = (LPBYTE)XorValue; BlockSize = (DWORD)CASCLIB_MIN(cbInBuffer, 0x40); // Prepare the XOR constants for(i = 0; i < 16; i++) { XorValue[i] = KeyMirror[i] + pState->Key[i]; } // Decrypt the block for(i = 0; i < BlockSize; i++) { pbOutBuffer[i] = pbInBuffer[i] ^ pbXorValue[i]; } pState->Key[8] = pState->Key[8] + 1; if(pState->Key[8] == 0) pState->Key[9] = pState->Key[9] + 1; // Adjust buffers pbOutBuffer += BlockSize; pbInBuffer += BlockSize; cbInBuffer -= BlockSize; } return ERROR_SUCCESS; } static int Decrypt_Salsa20(LPBYTE pbOutBuffer, LPBYTE pbInBuffer, size_t cbInBuffer, LPBYTE pbKey, DWORD cbKeySize, LPBYTE pbVector) { CASC_SALSA20 SalsaState; Initialize(&SalsaState, pbKey, cbKeySize, pbVector); return Decrypt(&SalsaState, pbOutBuffer, pbInBuffer, cbInBuffer); } //----------------------------------------------------------------------------- // Key map implementation static PCASC_ENCRYPTION_KEY2 CreateKeyItem(ULONGLONG KeyName, LPBYTE Key) { PCASC_ENCRYPTION_KEY2 pNewItem; if((pNewItem = new CASC_ENCRYPTION_KEY2) != NULL) { memset(pNewItem, 0, sizeof(CASC_ENCRYPTION_KEY2)); pNewItem->KeyName = KeyName; memcpy(pNewItem->Key, Key, CASC_KEY_LENGTH); } return pNewItem; } CASC_KEY_MAP::CASC_KEY_MAP() { memset(HashTable, 0, sizeof(HashTable)); } CASC_KEY_MAP::~CASC_KEY_MAP() { PCASC_ENCRYPTION_KEY2 pNextItem; PCASC_ENCRYPTION_KEY2 pKeyItem; for(size_t i = 0; i < CASC_KEY_TABLE_SIZE; i++) { if((pKeyItem = (PCASC_ENCRYPTION_KEY2)HashTable[i]) != NULL) { while(pKeyItem != NULL) { pNextItem = pKeyItem->pNext; delete pKeyItem; pKeyItem = pNextItem; } } } } LPBYTE CASC_KEY_MAP::FindKey(ULONGLONG KeyName) { PCASC_ENCRYPTION_KEY2 pKeyItem = NULL; size_t HashIndex = (size_t)(KeyName & CASC_KEY_TABLE_MASK); // Check the key chain beginning at the hash table pKeyItem = (PCASC_ENCRYPTION_KEY2)HashTable[HashIndex]; while(pKeyItem != NULL) { if(pKeyItem->KeyName == KeyName) return pKeyItem->Key; pKeyItem = pKeyItem->pNext; } // Not found return NULL; } bool CASC_KEY_MAP::AddKey(ULONGLONG KeyName, LPBYTE Key) { PCASC_ENCRYPTION_KEY2 pKeyItem; PCASC_ENCRYPTION_KEY2 pNewItem; size_t HashIndex = (size_t)(KeyName & CASC_KEY_TABLE_MASK); // Is the key already there? if(FindKey(KeyName) == NULL) { // Create new key item if((pNewItem = CreateKeyItem(KeyName, Key)) == NULL) return false; if(HashTable[HashIndex] != NULL) { // Get the last-in-chain key item pKeyItem = (PCASC_ENCRYPTION_KEY2)(HashTable[HashIndex]); while(pKeyItem->pNext != NULL) pKeyItem = pKeyItem->pNext; // Insert the key to the chain pKeyItem->pNext = pNewItem; return true; } else { HashTable[HashIndex] = pNewItem; return true; } } // Already exists, it's OK return true; } //----------------------------------------------------------------------------- // Public functions DWORD CascLoadEncryptionKeys(TCascStorage * hs) { for(size_t i = 0; i < _countof(StaticCascKeys); i++) { if(!hs->KeyMap.AddKey(StaticCascKeys[i].KeyName, StaticCascKeys[i].Key)) { return ERROR_NOT_ENOUGH_MEMORY; } } return ERROR_SUCCESS; } bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key) { TCascStorage * hs; // Validate the storage handle hs = TCascStorage::IsValid(hStorage); if (hs == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // Add the key to the map and return result return hs->KeyMap.AddKey(KeyName, Key); } bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey) { BYTE Key[CASC_KEY_LENGTH]; // Check the length of the string key if(strlen(szKey) != CASC_KEY_LENGTH * 2) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Convert the string key to the binary array if(BinaryFromString(szKey, CASC_KEY_LENGTH * 2, Key) != ERROR_SUCCESS) { SetCascError(ERROR_INVALID_PARAMETER); return false; } return CascAddEncryptionKey(hStorage, KeyName, Key); } LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName) { TCascStorage * hs; // Validate the storage handle hs = TCascStorage::IsValid(hStorage); if (hs == NULL) { SetCascError(ERROR_INVALID_HANDLE); return NULL; } // Return the result from the map's search function return hs->KeyMap.FindKey(KeyName); } bool WINAPI CascGetNotFoundEncryptionKey(HANDLE hStorage, ULONGLONG * KeyName) { TCascStorage * hs; // Validate the storage handle if ((hs = TCascStorage::IsValid(hStorage)) == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // If there was no decryption key error, just return false with ERROR_SUCCESS if(hs->LastFailKeyName == 0) { SetCascError(ERROR_SUCCESS); return false; } // Give the name of the key that failed most recently KeyName[0] = hs->LastFailKeyName; return true; } bool WINAPI CascImportKeysFromString(HANDLE hStorage, LPCSTR szKeyList) { // Verify parameters if(TCascStorage::IsValid(hStorage) == NULL || szKeyList == NULL || szKeyList[0] == 0) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Parse text file while(szKeyList[0]) { ULONGLONG KeyName = 0; DWORD dwErrCode; BYTE KeyValue[CASC_KEY_LENGTH]; // Capture key name dwErrCode = ConvertStringToInt(szKeyList, 0, KeyName, &szKeyList); if(dwErrCode != ERROR_SUCCESS) { SetCascError(dwErrCode); return false; } // TACT key list downloaded from https://wow.tools/api.php?type=tactkeys ends with a single zero if(KeyName == 0) break; // We only expect spaces and tabs at this point. Anything else will lead // to end of the loop. while(szKeyList[0] == 0x09 || szKeyList[0] == 0x20) szKeyList++; if(szKeyList[0] == 0x0A || szKeyList[0] == 0x0D) break; // Convert the string to binary dwErrCode = BinaryFromString(szKeyList, CASC_KEY_LENGTH * 2, KeyValue); if(dwErrCode != ERROR_SUCCESS) { SetCascError(dwErrCode); return false; } // Add the encryption key. Note that if the key already exists with the same value, // CascAddEncryptionKey will consider it success. if(!CascAddEncryptionKey(hStorage, KeyName, KeyValue)) return false; // Move to the next key while(szKeyList[0] != 0 && szKeyList[0] != 0x0A && szKeyList[0] != 0x0D) szKeyList++; while(szKeyList[0] == 0x0A || szKeyList[0] == 0x0D) szKeyList++; } return true; } bool WINAPI CascImportKeysFromFile(HANDLE hStorage, LPCTSTR szFileName) { TFileStream * pFileStream; ULONGLONG FileSize = 0; LPSTR szKeyList; bool bResult = false; // Open the file if((pFileStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY)) != NULL) { // Load the entire file to memory, up to 10 MB if(FileStream_GetSize(pFileStream, &FileSize) && FileSize < 0xA00000) { DWORD FileSize32 = (DWORD)FileSize; // Allocate buffer for the key stream if((szKeyList = CASC_ALLOC(FileSize32 + 1)) != NULL) { // Read the entire file and terminate it with zero FileStream_Read(pFileStream, NULL, szKeyList, FileSize32); szKeyList[FileSize] = 0; // Import the buffer bResult = CascImportKeysFromString(hStorage, szKeyList); CASC_FREE(szKeyList); } } else SetCascError(ERROR_FILE_TOO_LARGE); FileStream_Close(pFileStream); } return bResult; } DWORD CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer) { // Check the buffer size if((cbInBuffer - 1) > pcbOutBuffer[0]) return ERROR_INSUFFICIENT_BUFFER; // Copy the data memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); pcbOutBuffer[0] = cbInBuffer; return ERROR_SUCCESS; } DWORD CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex) { ULONGLONG KeyName = 0; LPBYTE pbBufferEnd = pbInBuffer + cbInBuffer; LPBYTE pbKey; DWORD KeyNameSize; DWORD dwShift = 0; DWORD IVSize; BYTE Vector[0x08]; BYTE EncryptionType; DWORD dwErrCode; // Verify and retrieve the key name size if(pbInBuffer >= pbBufferEnd) return ERROR_FILE_CORRUPT; if(pbInBuffer[0] != 0 && pbInBuffer[0] != 8) return ERROR_NOT_SUPPORTED; KeyNameSize = *pbInBuffer++; // Copy the key name if((pbInBuffer + KeyNameSize) >= pbBufferEnd) return ERROR_FILE_CORRUPT; memcpy(&KeyName, pbInBuffer, KeyNameSize); pbInBuffer += KeyNameSize; // Verify and retrieve the Vector size if(pbInBuffer >= pbBufferEnd) return ERROR_FILE_CORRUPT; if(pbInBuffer[0] != 4 && pbInBuffer[0] != 8) return ERROR_NOT_SUPPORTED; IVSize = *pbInBuffer++; // Copy the initialization vector if((pbInBuffer + IVSize) >= pbBufferEnd) return ERROR_FILE_CORRUPT; memset(Vector, 0, sizeof(Vector)); memcpy(Vector, pbInBuffer, IVSize); pbInBuffer += IVSize; // Verify and retrieve the encryption type if(pbInBuffer >= pbBufferEnd) return ERROR_FILE_CORRUPT; if(pbInBuffer[0] != 'S' && pbInBuffer[0] != 'A') return ERROR_NOT_SUPPORTED; EncryptionType = *pbInBuffer++; // Do we have enough space in the output buffer? if((DWORD)(pbBufferEnd - pbInBuffer) > pcbOutBuffer[0]) return ERROR_INSUFFICIENT_BUFFER; // Check if we know the key pbKey = hs->KeyMap.FindKey(KeyName); if(pbKey == NULL) { hs->LastFailKeyName = KeyName; return ERROR_FILE_ENCRYPTED; } // Shuffle the Vector with the block index // Note that there's no point to go beyond 32 bits, unless the file has // more than 0xFFFFFFFF frames. for(size_t i = 0; i < sizeof(dwFrameIndex); i++) { Vector[i] = Vector[i] ^ (BYTE)((dwFrameIndex >> dwShift) & 0xFF); dwShift += 8; } // Perform the decryption-specific action switch(EncryptionType) { case 'S': // Salsa20 dwErrCode = Decrypt_Salsa20(pbOutBuffer, pbInBuffer, (pbBufferEnd - pbInBuffer), pbKey, 0x10, Vector); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Supply the size of the output buffer pcbOutBuffer[0] = (DWORD)(pbBufferEnd - pbInBuffer); return ERROR_SUCCESS; // case 'A': // return ERROR_NOT_SUPPORTED; } assert(false); return ERROR_NOT_SUPPORTED; } ================================================ FILE: deps/CascLib/src/CascDumpData.cpp ================================================ /*****************************************************************************/ /* CascDumpData.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* System-dependent directory functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 07.05.14 1.00 Lad The first version of CascDumpData.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" #ifdef _DEBUG // The entire feature is only valid for debug purposes //----------------------------------------------------------------------------- // Forward definitions int CaptureEncodingHeader(CASC_ENCODING_HEADER & EnHeader, LPBYTE pbFileData, size_t cbFileData); int CaptureDownloadHeader(CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbFileData, size_t cbFileData); int CaptureDownloadEntry(CASC_DOWNLOAD_HEADER & DlHeader, CASC_DOWNLOAD_ENTRY & DlEntry, LPBYTE pbFilePtr, LPBYTE pbFileEnd); int CaptureDownloadTag(CASC_DOWNLOAD_HEADER & DlHeader, CASC_TAG_ENTRY1 & DlTag, LPBYTE pbFilePtr, LPBYTE pbFileEnd); //----------------------------------------------------------------------------- // Local functions static char * StringFromLPTSTR(LPCTSTR szString, char * szBuffer, size_t cchBuffer) { char * szSaveBuffer = szBuffer; char * szBufferEnd = szBuffer + cchBuffer - 1; while (szBuffer < szBufferEnd && szString[0] != 0) *szBuffer++ = (char)*szString++; szBuffer[0] = 0; return szSaveBuffer; } // Either opens a dump file or returns "stdout" static FILE * OpenDumpFile(const char * szDumpFile) { if(szDumpFile != NULL) { return fopen(szDumpFile, "wt"); } else { return stdout; } } // Closes the dump file if it was open by OpenDumpFile static void CloseDumpFile(const char * szDumpFile, FILE * fp) { if(szDumpFile != NULL && fp != NULL) fclose(fp); } static void DumpKey(FILE * fp, const char * szInFormat, LPBYTE pbData, size_t cbData) { const char * szFormatSpec; LPBYTE pbDataEnd = pbData + cbData; char szFormat[0x100]; char szKey[MD5_STRING_SIZE + 1]; // First line: Copy the line with the complete format CascStrCopy(szFormat, _countof(szFormat), szInFormat); szFormatSpec = strstr(szFormat, "%s"); // Dump all keys, every one on a new line while(pbData < pbDataEnd) { // Fump the line StringFromBinary(pbData, MD5_HASH_SIZE, szKey); fprintf(fp, szFormat, szKey); // If there will be more lines, then we clear the entire part until "%s" if(szFormatSpec != NULL) memset(szFormat, ' ', (szFormatSpec - szFormat)); // Move pointers pbData += MD5_HASH_SIZE; } } /* void CascDumpSparseArray(const char * szFileName, void * pvSparseArray) { TSparseArray * pSparseArray = (TSparseArray *)pvSparseArray; FILE * fp; // Create the dump file fp = fopen(szFileName, "wt"); if(fp != NULL) { // Write header fprintf(fp, "## Value\n-- -----\n"); // Write the values for(DWORD i = 0; i < pSparseArray->TotalItemCount; i++) { DWORD Value = 0; if(pSparseArray->IsItemPresent(i)) { Value = pSparseArray->GetItemValue(i); fprintf(fp, "%02X %02X\n", i, Value); } else { fprintf(fp, "%02X --\n", i); } } fclose(fp); } } void CascDumpNameFragTable(const char * szFileName, void * pMarFile) { TFileNameDatabase * pDB = ((PMAR_FILE)pMarFile)->pDatabasePtr->pDB; FILE * fp; // Create the dump file fp = fopen(szFileName, "wt"); if(fp != NULL) { PNAME_FRAG pNameTable = pDB->NameFragTable.ItemArray; const char * szNames = pDB->IndexStruct_174.ItemArray; const char * szLastEntry; char szMatchType[0x100]; // Dump the table header fprintf(fp, "Indx ThisHash NextHash FragOffs\n"); fprintf(fp, "---- -------- -------- --------\n"); // Dump all name entries for(DWORD i = 0; i < pDB->NameFragTable.ItemCount; i++) { // Reset both match types szMatchType[0] = 0; szLastEntry = ""; // Only if the table entry is not empty if(pNameTable->ItemIndex != 0xFFFFFFFF) { // Prepare the entry if(IS_SINGLE_CHAR_MATCH(pDB->NameFragTable, i)) CascStrPrintf(szMatchType, _countof(szMatchType), "SINGLE_CHAR (\'%c\')", (pNameTable->FragOffs & 0xFF)); else CascStrPrintf(szMatchType, _countof(szMatchType), "NAME_FRAGMT (\"%s\")", szNames + pNameTable->FragOffs); } // Dump the entry fprintf(fp, "0x%02X %08x %08x %08x %s%s\n", i, pNameTable->ItemIndex, pNameTable->NextIndex, pNameTable->FragOffs, szMatchType, szLastEntry); pNameTable++; } fclose(fp); } } void CascDumpFileNames(const char * szFileName, void * pvMarFile) { TMndxFindResult Struct1C; PMAR_FILE pMarFile = (PMAR_FILE)pvMarFile; FILE * fp; char szNameBuff[0x200]; bool bFindResult; // Create the dump file fp = fopen(szFileName, "wt"); if(fp != NULL) { // Set an empty path as search mask (?) Struct1C.SetSearchPath("", 0); // Keep searching for(;;) { // Search the next file name pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C, &bFindResult); // Stop the search in case of failure if(!bFindResult) break; // Printf the found file name memcpy(szNameBuff, Struct1C.szFoundPath, Struct1C.cchFoundPath); szNameBuff[Struct1C.cchFoundPath] = 0; fprintf(fp, "%s\n", szNameBuff); } fclose(fp); } // Free the search structures Struct1C.FreeStruct40(); } void DumpEKeyEntries(TCascStorage * hs, FILE * fp) { // Dump header fprintf(fp, "=== Ekey Entries ============================================================\n"); // Dump index entries from all index files for(int file = 0; file < CASC_INDEX_COUNT; file++) { PCASC_EKEY_ENTRY pEKeyEntry = hs->IndexFile[file].pEKeyEntries; DWORD nEKeyEntries = hs->IndexFile[file].nEKeyEntries; // Only if they are present if(pEKeyEntry && nEKeyEntries) { // fprintf(fp, "--- Index: %03u --------------------------------------------------------------\n", file); for(DWORD entry = 0; entry < nEKeyEntries; entry++, pEKeyEntry++) DumpEKeyEntry(fp, pEKeyEntry->EKey, CASC_EKEY_SIZE, pEKeyEntry->StorageOffset, ConvertBytesToInteger_4_LE(pEKeyEntry->EncodedSize)); } } // Dump tail fprintf(fp, "=============================================================================\n\n"); } */ //----------------------------------------------------------------------------- // Dumping the ENCODING manifest /* static void DumpESpecEntries(FILE * fp, LPBYTE pbDataPtr, LPBYTE pbDataEnd) { fprintf(fp, "--- ESpec Entries -----------------------------------------------------------\n"); while(pbDataPtr < pbDataEnd) { const char * szESpecEntry = (const char *)pbDataPtr; // Find the end of the entry while((pbDataPtr < pbDataEnd) && (pbDataPtr[0] != 0)) { pbDataPtr++; } // Dump the entry if(pbDataPtr[0] == 0) { fprintf(fp, "%s\n", szESpecEntry); pbDataPtr++; } } } static void DumpCKeyEntry(PCASC_CKEY_ENTRY pCKeyEntry, FILE * fp, DWORD nIndex) { LPBYTE pbEKey; char szStringBuff[MD5_STRING_SIZE + 1]; // Print the CKey and number of EKeys fprintf(fp, "[0x%02X] %s (EKeys: %02u): ", nIndex, StringFromBinary(pCKeyEntry->CKey, MD5_HASH_SIZE, szStringBuff), pCKeyEntry->EKeyCount); pbEKey = pCKeyEntry->EKey; // Print the EKeys for(USHORT i = 0; i < pCKeyEntry->EKeyCount; i++, pbEKey += MD5_HASH_SIZE) fprintf(fp, " %s ", StringFromBinary(pbEKey, MD5_HASH_SIZE, szStringBuff)); fprintf(fp, "\n"); } static void DumpCKeyPages(FILE * fp, CASC_ENCODING_HEADER & EnHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd) { // Get the CKey page header and the page itself PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)pbDataPtr; LPBYTE pbPageEntry = (LPBYTE)(pPageHeader + EnHeader.CKeyPageCount); fprintf(fp, "--- CKey Pages --------------------------------------------------------------\n"); for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++, pPageHeader++) { LPBYTE pbCKeyEntry = pbPageEntry; LPBYTE pbEndOfPage = pbPageEntry + EnHeader.CKeyPageSize; DWORD nCKeyIndex = 0; // Check if there is enough space in the buffer if((pbPageEntry + EnHeader.CKeyPageSize) > pbDataEnd) break; DumpKey(fp, "First CKey: %s\n", pPageHeader->FirstKey, EnHeader.CKeyLength); DumpKey(fp, "Page hash: %s\n", pPageHeader->SegmentHash, MD5_HASH_SIZE); fprintf(fp, "\n"); // Parse all CKey entries while(pbCKeyEntry <= pbEndOfPage) { PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)pbCKeyEntry; // Get pointer to the encoding entry if(pCKeyEntry->EKeyCount == 0) break; // Dump this encoding entry DumpCKeyEntry(pCKeyEntry, fp, nCKeyIndex++); // Move to the next encoding entry pbCKeyEntry += sizeof(FILE_CKEY_ENTRY) + ((pCKeyEntry->EKeyCount - 1) * EnHeader.CKeyLength); } // Move to the next segment pbPageEntry += EnHeader.CKeyPageSize; fprintf(fp, "\n"); } } void DumpEncodingManifest(TCascStorage * hs, LPBYTE pbData, ULONG cbData, FILE * fp) { // Dump header fprintf(fp, "=== ENCODING Manifest =======================================================\n"); // Dump the encoding file if(hs->EncodingManifest.pbData && hs->EncodingManifest.cbData) { CASC_ENCODING_HEADER EnHeader; LPBYTE pbFilePtr = hs->EncodingManifest.pbData; DWORD cbDataSize; // Capture the header of the ENCODING file if(CaptureEncodingHeader(EnHeader, hs->EncodingManifest.pbData, hs->EncodingManifest.cbData) == ERROR_SUCCESS) { // Dump the ENCODING file info fprintf(fp, "Magic: %u\n", EnHeader.Magic); fprintf(fp, "Version: %u\n", EnHeader.Version); fprintf(fp, "CKey Length: %02X\n", EnHeader.CKeyLength); fprintf(fp, "EKey Length: %02X\n", EnHeader.EKeyLength); fprintf(fp, "CKey page size: %08X\n", EnHeader.CKeyPageSize); fprintf(fp, "CKey page count: %08X\n", EnHeader.CKeyPageCount); fprintf(fp, "EKey page size: %08X\n", EnHeader.EKeyPageSize); fprintf(fp, "EKey page count: %08X\n", EnHeader.EKeyPageCount); fprintf(fp, "ESpec block size: %08X\n", EnHeader.ESpecBlockSize); pbFilePtr += sizeof(FILE_ENCODING_HEADER); // Dump all ESpec entries DumpESpecEntries(fp, pbFilePtr, pbFilePtr + EnHeader.ESpecBlockSize); pbFilePtr += EnHeader.ESpecBlockSize; // Parse all CKey pages and dump them cbDataSize = EnHeader.CKeyPageCount * (sizeof(FILE_CKEY_PAGE) + EnHeader.CKeyPageSize); DumpCKeyPages(fp, EnHeader, pbFilePtr, pbFilePtr + cbDataSize); pbFilePtr += cbDataSize; } } else { fprintf(fp, "(empty)\n"); } // Dump tail fprintf(fp, "=============================================================================\n\n"); } */ //----------------------------------------------------------------------------- // Dumping the DOWNLOAD manifest /* static void DumpDownloadEntries(FILE * fp, CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbDownloadPtr, LPBYTE pbDownloadEnd) { fprintf(fp, "--- DOWNLOAD Entries --------------------------------------------------------\n"); fprintf(fp, "Index EKey FileSize Checksum Flags Prio\n"); fprintf(fp, "-------- -------------------------------- ---------- -------- -------- ----\n"); for(DWORD i = 0; i < DlHeader.EntryCount; i++) { CASC_DOWNLOAD_ENTRY DlEntry; char szEKey[MD5_STRING_SIZE+1]; char szChksum[0x40]; char szFlags[0x40]; if(CaptureDownloadEntry(DlHeader, DlEntry, pbDownloadPtr, pbDownloadEnd) != ERROR_SUCCESS) break; // Dump the entry StringFromBinary(DlEntry.EKey, DlHeader.EKeyLength, szEKey); CascStrPrintf(szChksum, _countof(szChksum), (DlHeader.EntryHasChecksum ? "%08X" : " "), DlEntry.Checksum); CascStrPrintf(szFlags, _countof(szFlags), (DlHeader.FlagByteSize ? "%08X" : " "), DlEntry.Flags); fprintf(fp, "[%06X] %-16s %010I64X %s %s %04X\n", i, szEKey, DlEntry.FileSize, szChksum, szFlags, DlEntry.Priority); // Move to the next entry pbDownloadPtr += DlHeader.EntryLength; } fprintf(fp, "\n"); } static void DumpDownloadTags(FILE * fp, CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbFilePtr, LPBYTE pbFileEnd) { CASC_TAG_ENTRY1 DlTag; fprintf(fp, "--- DOWNLOAD Tags ---------------------------------------------------------\n"); for(DWORD i = 0; i < DlHeader.TagCount; i++) { // Capture the download tag if(CaptureDownloadTag(DlHeader, DlTag, pbFilePtr, pbFileEnd) != ERROR_SUCCESS) break; // Dump the tag fprintf(fp, "[%02X]: %08X = %s\n", i, DlTag.TagValue, DlTag.szTagName); pbFilePtr += DlTag.TagLength; } } void DumpDownloadManifest(TCascStorage * hs, FILE * fp) { // Dump header fprintf(fp, "=== DOWNLOAD Manifest =======================================================\n"); // Dump the encoding file if(hs->DownloadManifest.pbData && hs->DownloadManifest.cbData) { CASC_DOWNLOAD_HEADER DlHeader; LPBYTE pbFileEnd = hs->DownloadManifest.pbData + hs->DownloadManifest.cbData; LPBYTE pbFilePtr = hs->DownloadManifest.pbData; // Capture the header of the ENCODING file if(CaptureDownloadHeader(DlHeader, hs->DownloadManifest.pbData, hs->DownloadManifest.cbData) == ERROR_SUCCESS) { // Dump the DOWNLOAD header fprintf(fp, "Magic: %u\n", DlHeader.Magic); fprintf(fp, "Version: %u\n", DlHeader.Version); fprintf(fp, "EKey Length: %02X\n", DlHeader.EKeyLength); fprintf(fp, "Checksum present: %s\n", DlHeader.EntryHasChecksum ? "Yes" : "No"); fprintf(fp, "Entry Count: %08X\n", DlHeader.EntryCount); fprintf(fp, "Tag Count: %08X\n", DlHeader.TagCount); fprintf(fp, "Flag byte size: %08X\n", DlHeader.FlagByteSize); fprintf(fp, "Base priority: %08X\n", DlHeader.BasePriority); fprintf(fp, "Header length: %08X\n", (DWORD)DlHeader.HeaderLength); fprintf(fp, "Entry length: %08X\n", (DWORD)DlHeader.EntryLength); fprintf(fp, "\n"); pbFilePtr += DlHeader.HeaderLength; // Dump all download entries DumpDownloadEntries(fp, DlHeader, pbFilePtr, pbFileEnd); pbFilePtr += DlHeader.EntryLength * DlHeader.EntryCount; // Dump all tags DumpDownloadTags(fp, DlHeader, pbFilePtr, pbFileEnd); } } else { fprintf(fp, "(empty)\n"); } // Dump tail fprintf(fp, "=============================================================================\n\n"); } */ //----------------------------------------------------------------------------- // Public dumping functions void CascDumpFile(const char * szDumpFile, HANDLE hFile) { FILE * fp; DWORD dwBytesRead = 1; DWORD dwFilePos = CascSetFilePointer(hFile, 0, NULL, FILE_BEGIN); BYTE Buffer[0x1000]; // Create/open the dump file fp = OpenDumpFile(szDumpFile); if(fp != NULL) { // Read data as long as we can, write as long as we can while(dwBytesRead != 0) { // Read from the source file if(!CascReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead)) break; // Write to the destination file if(fwrite(Buffer, 1, dwBytesRead, fp) != dwBytesRead) break; } // Restore the file pointer CascSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN); // Close the dump file CloseDumpFile(szDumpFile, fp); } } void CascDumpStorage(HANDLE hStorage, const char * szDumpFile) { TCascStorage * hs; FILE * fp = stdout; char szStringBuff[0x800]; // Verify the storage handle hs = TCascStorage::IsValid(hStorage); if(hs == NULL) return; // Create/open the dump file fp = OpenDumpFile(szDumpFile); if(fp != NULL) { // Dump the basic storage info fprintf(fp, "=== Basic Storage Info ======================================================\n"); fprintf(fp, "DataPath: %s\n", StringFromLPTSTR(hs->szDataPath, szStringBuff, sizeof(szStringBuff))); fprintf(fp, "IndexPath: %s\n", StringFromLPTSTR(hs->szIndexPath, szStringBuff, sizeof(szStringBuff))); fprintf(fp, "BuildFile: %s\n", StringFromLPTSTR(hs->szBuildFile, szStringBuff, sizeof(szStringBuff))); fprintf(fp, "CDN Server: %s\n", StringFromLPTSTR(hs->szCdnServers, szStringBuff, sizeof(szStringBuff))); fprintf(fp, "CDN Path: %s\n", StringFromLPTSTR(hs->szCdnPath, szStringBuff, sizeof(szStringBuff))); DumpKey(fp, "CDN Config Key: %s\n", hs->CdnConfigKey.pbData, hs->CdnConfigKey.cbData); DumpKey(fp, "CDN Build Key: %s\n", hs->CdnBuildKey.pbData, hs->CdnBuildKey.cbData); DumpKey(fp, "Archives Key: %s\n", hs->ArchivesKey.pbData, hs->ArchivesKey.cbData); DumpKey(fp, "ROOT file: %s\n", hs->RootFile.CKey, CASC_CKEY_SIZE); DumpKey(fp, "PATCH file: %s\n", hs->PatchFile.CKey, CASC_CKEY_SIZE); DumpKey(fp, "ENCODING file: %s\n", hs->EncodingCKey.CKey, CASC_CKEY_SIZE); DumpKey(fp, "DOWNLOAD file: %s\n", hs->DownloadCKey.CKey, CASC_CKEY_SIZE); DumpKey(fp, "INSTALL file: %s\n", hs->InstallCKey.CKey, CASC_CKEY_SIZE); fprintf(fp, "\n"); // Dump the complete ENCODING manifest // DumpEncodingManifest(hs, fp); // Dump the complete ENCODING manifest // DumpDownloadManifest(hs, fp); // Close the dump file CloseDumpFile(szDumpFile, fp); } } #else // _DEBUG // so linker won't mind this .cpp file is empty in non-DEBUG builds void unused_symbol() { } #endif // _DEBUG ================================================ FILE: deps/CascLib/src/CascFiles.cpp ================================================ /*****************************************************************************/ /* CascFiles.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Various text file parsers */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad The first version of CascBuildCfg.cpp */ /* 30.10.15 1.00 Lad Renamed to CascFiles.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") // Internet functions for HTTP stream #endif //----------------------------------------------------------------------------- // Local defines typedef DWORD (*PARSECSVFILE)(TCascStorage * hs, CASC_CSV & Csv); typedef DWORD (*PARSETEXTFILE)(TCascStorage * hs, void * pvListFile); typedef DWORD (*PARSE_VARIABLE)(TCascStorage * hs, const char * szVariableName, const char * szDataBegin, const char * szDataEnd, void * pvParam); #define MAX_VAR_NAME 80 //----------------------------------------------------------------------------- // Local structures struct TBuildFileInfo { LPCTSTR szFileName; CBLD_TYPE BuildFileType; }; struct TGameLocaleString { const char * szLocale; DWORD dwLocale; }; static const TBuildFileInfo BuildTypes[] = { {_T(".build.info"), CascBuildInfo}, // Since HOTS build 30027, the game uses .build.info file for storage info {_T(".build.db"), CascBuildDb}, // Older CASC storages {_T("versions"), CascVersionsDb}, // Older CASC storages {NULL, CascBuildNone} }; static LPCTSTR DataDirs[] = { _T("data") _T(PATH_SEP_STRING) _T("casc"), // Overwatch _T("data"), // TACT casc (for Linux systems) _T("Data"), // World of Warcraft, Diablo _T("SC2Data"), // Starcraft II (Legacy of the Void) build 38749 _T("HeroesData"), // Heroes of the Storm _T("BNTData"), // Heroes of the Storm, until build 30414 NULL, }; static LPCTSTR bnet_region = _T("us"); //----------------------------------------------------------------------------- // Local functions static bool inline IsWhiteSpace(const char * szVarValue) { return (0 <= szVarValue[0] && szVarValue[0] <= 0x20); } static bool inline IsValueSeparator(const char * szVarValue) { return (IsWhiteSpace(szVarValue) || (szVarValue[0] == '|')); } static bool IsCharDigit(BYTE OneByte) { return ('0' <= OneByte && OneByte <= '9'); } static bool StringEndsWith(LPCSTR szString, size_t nLength, LPCSTR szSuffix, size_t nSuffixLength) { return ((nLength > nSuffixLength) && !strcmp(szString + nLength - nSuffixLength, szSuffix)); } static const char * CaptureDecimalInteger(const char * szDataPtr, const char * szDataEnd, PDWORD PtrValue) { const char * szSaveDataPtr = szDataPtr; DWORD TotalValue = 0; DWORD AddValue = 0; // Skip all spaces while (szDataPtr < szDataEnd && szDataPtr[0] == ' ') szDataPtr++; // Load the number while (szDataPtr < szDataEnd && szDataPtr[0] != ' ') { // Must only contain decimal digits ('0' - '9') if(!IsCharDigit(szDataPtr[0])) break; // Get the next value and verify overflow AddValue = szDataPtr[0] - '0'; if((TotalValue + AddValue) < TotalValue) return NULL; TotalValue = (TotalValue * 10) + AddValue; szDataPtr++; } // If there were no decimal digits, we consider it failure if(szDataPtr == szSaveDataPtr) return NULL; // Give the result PtrValue[0] = TotalValue; return szDataPtr; } static const char * CaptureSingleString(const char * szDataPtr, const char * szDataEnd, char * szBuffer, size_t ccBuffer) { char * szBufferEnd = szBuffer + ccBuffer - 1; // Skip all whitespaces while (szDataPtr < szDataEnd && IsWhiteSpace(szDataPtr)) szDataPtr++; // Copy the string while (szDataPtr < szDataEnd && szBuffer < szBufferEnd && !IsWhiteSpace(szDataPtr) && szDataPtr[0] != '=') *szBuffer++ = *szDataPtr++; szBuffer[0] = 0; return szDataPtr; } static const char * CaptureSingleHash(const char * szDataPtr, const char * szDataEnd, LPBYTE HashValue, size_t HashLength) { const char * szHashString; size_t HashStringLength = HashLength * 2; // Skip all whitespaces while (szDataPtr < szDataEnd && IsWhiteSpace(szDataPtr)) szDataPtr++; szHashString = szDataPtr; // Count all hash characters for (size_t i = 0; i < HashStringLength; i++) { if(szDataPtr >= szDataEnd || isxdigit(szDataPtr[0]) == 0) return NULL; szDataPtr++; } // There must be a separator or end-of-string if(szDataPtr > szDataEnd || IsWhiteSpace(szDataPtr) == false) return NULL; // Give the values BinaryFromString(szHashString, HashStringLength, HashValue); return szDataPtr; } static const char * CaptureHashCount(const char * szDataPtr, const char * szDataEnd, size_t * PtrHashCount) { BYTE HashValue[MD5_HASH_SIZE]; size_t HashCount = 0; // Capculate the hash count while (szDataPtr < szDataEnd) { // Check one hash szDataPtr = CaptureSingleHash(szDataPtr, szDataEnd, HashValue, MD5_HASH_SIZE); if(szDataPtr == NULL) return NULL; // Skip all whitespaces while (szDataPtr < szDataEnd && IsWhiteSpace(szDataPtr)) szDataPtr++; HashCount++; } // Give results PtrHashCount[0] = HashCount; return szDataPtr; } static DWORD GetLocaleValue(LPCSTR szTag) { DWORD Language = 0; // Convert the string language to integer Language = (Language << 0x08) | szTag[0]; Language = (Language << 0x08) | szTag[1]; Language = (Language << 0x08) | szTag[2]; Language = (Language << 0x08) | szTag[3]; // Language-specific action switch(Language) { case 0x656e5553: return CASC_LOCALE_ENUS; case 0x656e4742: return CASC_LOCALE_ENGB; case 0x656e434e: return CASC_LOCALE_ENCN; case 0x656e5457: return CASC_LOCALE_ENTW; case 0x65734553: return CASC_LOCALE_ESES; case 0x65734d58: return CASC_LOCALE_ESMX; case 0x70744252: return CASC_LOCALE_PTBR; case 0x70745054: return CASC_LOCALE_PTPT; case 0x7a68434e: return CASC_LOCALE_ZHCN; case 0x7a685457: return CASC_LOCALE_ZHTW; case 0x6b6f4b52: return CASC_LOCALE_KOKR; case 0x66724652: return CASC_LOCALE_FRFR; case 0x64654445: return CASC_LOCALE_DEDE; case 0x72755255: return CASC_LOCALE_RURU; case 0x69744954: return CASC_LOCALE_ITIT; } return 0; } static bool CheckConfigFileVariable( TCascStorage * hs, // Pointer to storage structure const char * szLinePtr, // Pointer to the begin of the line const char * szLineEnd, // Pointer to the end of the line const char * szVarName, // Pointer to the variable to check PARSE_VARIABLE PfnParseProc, // Pointer to the parsing function void * pvParseParam) // Pointer to the parameter passed to parsing function { char szVariableName[MAX_VAR_NAME]; // Capture the variable from the line szLinePtr = CaptureSingleString(szLinePtr, szLineEnd, szVariableName, sizeof(szVariableName)); if(szLinePtr == NULL) return false; // Verify whether this is the variable if(!CascCheckWildCard(szVariableName, szVarName)) return false; // Skip the spaces and '=' while (szLinePtr < szLineEnd && (IsWhiteSpace(szLinePtr) || szLinePtr[0] == '=')) szLinePtr++; // Call the parsing function only if there is some data if(szLinePtr >= szLineEnd) return false; return (PfnParseProc(hs, szVariableName, szLinePtr, szLineEnd, pvParseParam) == ERROR_SUCCESS); } static DWORD LoadHashArray( PQUERY_KEY pBlob, const char * szLinePtr, const char * szLineEnd, size_t HashCount) { DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Allocate the blob buffer pBlob->cbData = (DWORD)(HashCount * MD5_HASH_SIZE); pBlob->pbData = CASC_ALLOC(pBlob->cbData); if(pBlob->pbData != NULL) { LPBYTE pbBuffer = pBlob->pbData; for (size_t i = 0; i < HashCount; i++) { // Capture the hash value szLinePtr = CaptureSingleHash(szLinePtr, szLineEnd, pbBuffer, MD5_HASH_SIZE); if(szLinePtr == NULL) return ERROR_BAD_FORMAT; // Move buffer pbBuffer += MD5_HASH_SIZE; } dwErrCode = ERROR_SUCCESS; } return dwErrCode; } static DWORD LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd) { size_t HashCount = 0; DWORD dwErrCode = ERROR_SUCCESS; // Retrieve the hash count if(CaptureHashCount(szLineBegin, szLineEnd, &HashCount) == NULL) return ERROR_BAD_FORMAT; // Do nothing if there is no data if(HashCount != 0) { dwErrCode = LoadHashArray(pBlob, szLineBegin, szLineEnd, HashCount); } return dwErrCode; } // Loads a query key from the text form // A QueryKey is an array of "ContentKey EncodedKey1 ... EncodedKeyN" static DWORD LoadQueryKey(TCascStorage * /* hs */, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * pvParam) { return LoadMultipleHashes((PQUERY_KEY)pvParam, szDataBegin, szDataEnd); } static DWORD LoadCKeyEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam) { PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)pvParam; size_t nLength = strlen(szVariableName); size_t HashCount = 0; // Ignore "xxx-config" items if(StringEndsWith(szVariableName, nLength, "-config", 7)) return ERROR_SUCCESS; // If the variable ends at "-size", it means we need to capture the size if(StringEndsWith(szVariableName, nLength, "-size", 5)) { DWORD ContentSize = CASC_INVALID_SIZE; DWORD EncodedSize = CASC_INVALID_SIZE; // Load the content size szDataPtr = CaptureDecimalInteger(szDataPtr, szDataEnd, &ContentSize); if(szDataPtr == NULL) return ERROR_BAD_FORMAT; CaptureDecimalInteger(szDataPtr, szDataEnd, &EncodedSize); pCKeyEntry->ContentSize = ContentSize; pCKeyEntry->EncodedSize = EncodedSize; return ERROR_SUCCESS; } // If the string doesn't end with "-config", assume it's the CKey/EKey else { // Get the number of hashes. It is expected to be 1 or 2 if(CaptureHashCount(szDataPtr, szDataEnd, &HashCount) != NULL) { // Capture the CKey if(HashCount >= 1) { // Load the CKey. This should alway be there szDataPtr = CaptureSingleHash(szDataPtr, szDataEnd, pCKeyEntry->CKey, MD5_HASH_SIZE); if(szDataPtr == NULL) return ERROR_BAD_FORMAT; pCKeyEntry->Flags |= CASC_CE_HAS_CKEY; } // Capture EKey, if any if(HashCount == 2) { // Load the EKey into the structure szDataPtr = CaptureSingleHash(szDataPtr, szDataEnd, pCKeyEntry->EKey, MD5_HASH_SIZE); if(szDataPtr == NULL) return ERROR_BAD_FORMAT; pCKeyEntry->Flags |= CASC_CE_HAS_EKEY; // Increment the number of EKey entries loaded from text build file hs->EKeyEntries++; } return (HashCount == 1 || HashCount == 2) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } } // Unrecognized return ERROR_BAD_FORMAT; } // For files like PATCH, which only contain EKey in the build file static DWORD LoadEKeyEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam) { PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)pvParam; DWORD dwErrCode; // Load the entry as if it was a CKey. Then move CKey into EKey and adjust flags if((dwErrCode = LoadCKeyEntry(hs, szVariableName, szDataPtr, szDataEnd, pvParam)) == ERROR_SUCCESS) { if((pCKeyEntry->Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL)) == CASC_CE_HAS_CKEY) { // Move the CKey into EKey CopyMemory16(pCKeyEntry->EKey, pCKeyEntry->CKey); ZeroMemory16(pCKeyEntry->CKey); // Adjust flags pCKeyEntry->Flags = (pCKeyEntry->Flags & ~CASC_CE_HAS_CKEY) | CASC_CE_HAS_EKEY; } } return dwErrCode; } static DWORD LoadVfsRootEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam) { PCASC_CKEY_ENTRY pCKeyEntry; CASC_ARRAY * pArray = (CASC_ARRAY *)pvParam; const char * szVarPtr = szVariableName; const char * szVarEnd = szVarPtr + strlen(szVarPtr); DWORD VfsRootIndex = CASC_INVALID_INDEX; // Skip the "vfs-" part if(!strncmp(szVariableName, "vfs-", 4)) { // Then, there must be a decimal number as index if((szVarPtr = CaptureDecimalInteger(szVarPtr + 4, szVarEnd, &VfsRootIndex)) != NULL) { // We expect the array to be initialized assert(pArray->IsInitialized()); // The index of the key must not be NULL if(VfsRootIndex != 0) { // Make sure that he entry is in the array pCKeyEntry = (PCASC_CKEY_ENTRY)pArray->ItemAt(VfsRootIndex - 1); if(pCKeyEntry == NULL) { // Insert a new entry pCKeyEntry = (PCASC_CKEY_ENTRY)pArray->InsertAt(VfsRootIndex - 1); if(pCKeyEntry == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Initialize the new entry pCKeyEntry->Init(); } // Call just a normal parse function return LoadCKeyEntry(hs, szVariableName, szDataPtr, szDataEnd, pCKeyEntry); } } } return ERROR_SUCCESS; } static DWORD LoadBuildProductId(TCascStorage * hs, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * /* pvParam */) { size_t nLength = (szDataEnd - szDataBegin); if(hs->szCodeName == NULL) { if((hs->szCodeName = CASC_ALLOC(nLength + 1)) != NULL) { CascStrCopy(hs->szCodeName, nLength + 1, szDataBegin, nLength); } } return ERROR_SUCCESS; } // "B29049" // "WOW-18125patch6.0.1" // "30013_Win32_2_2_0_Ptr_ptr" // "prometheus-0_8_0_0-24919" static DWORD LoadBuildNumber(TCascStorage * hs, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * /* pvParam */) { DWORD dwBuildNumber = 0; DWORD dwMaxValue = 0; // Parse the string and take the largest decimal numeric value // "build-name = 1.21.5.4037-retail" while(szDataBegin < szDataEnd) { // There must be at least three digits (build 99 anyone?) if(IsCharDigit(szDataBegin[0])) { dwBuildNumber = (dwBuildNumber * 10) + (szDataBegin[0] - '0'); dwMaxValue = CASCLIB_MAX(dwBuildNumber, dwMaxValue); } else { // Reset build number when we find non-digit char dwBuildNumber = 0; } // Move to the next szDataBegin++; } // If not there, just take value from build file if((hs->dwBuildNumber = dwMaxValue) == 0) hs->dwBuildNumber = hs->CdnBuildKey.pbData[0] % 100; return ERROR_BAD_FORMAT; } static int LoadQueryKey(const CASC_CSV_COLUMN & Column, QUERY_KEY & Key) { // Check the input data if(Column.szValue == NULL) return ERROR_BUFFER_OVERFLOW; if(Column.nLength != MD5_STRING_SIZE) return ERROR_BAD_FORMAT; return LoadHashArray(&Key, Column.szValue, Column.szValue + Column.nLength, 1); } static void SetProductCodeName(TCascStorage * hs, LPCSTR szCodeName) { if(hs->szCodeName == NULL && szCodeName != NULL) { hs->szCodeName = CascNewStrA2T(szCodeName); } } static DWORD GetDefaultCdnPath(TCascStorage * hs, const CASC_CSV_COLUMN & Column) { if(hs->szCdnPath == NULL && Column.nLength != 0) { hs->szCdnPath = CascNewStrA2T(Column.szValue); } return ERROR_SUCCESS; } static DWORD GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Column) { LPCSTR szTagEnd = Column.szValue + Column.nLength - 4; LPCSTR szTagPtr = Column.szValue; DWORD dwLocaleMask = 0; while(szTagPtr < szTagEnd) { DWORD dwLocaleValue = GetLocaleValue(szTagPtr); if(dwLocaleValue != 0) { dwLocaleMask = dwLocaleMask | GetLocaleValue(szTagPtr); szTagPtr += 4; } else { szTagPtr++; } } hs->dwDefaultLocale = dwLocaleMask; return ERROR_SUCCESS; } static DWORD ParseFile_CDNS(TCascStorage * hs, CASC_CSV & Csv) { const char * szWantedRegion = hs->szRegion; const char * szRegion; size_t nLineCount; // Fix the region if(szWantedRegion == NULL || !_stricmp(szWantedRegion, "beta") || !_stricmp(szWantedRegion, "xx")) szWantedRegion = "us"; // Determine the row count nLineCount = Csv.GetLineCount(); // Find the active config for(size_t i = 0; i < nLineCount; i++) { // Retrieve the region if((szRegion = Csv[i]["Name!STRING:0"].szValue) != NULL) { // Is it the version we are looking for? if(!strcmp(Csv[i]["Name!STRING:0"].szValue, szWantedRegion)) { // Save the list of CDN servers hs->szCdnServers = CascNewStrA2T(Csv[i]["Hosts!STRING:0"].szValue); // Save the CDN subpath hs->szCdnPath = CascNewStrA2T(Csv[i]["Path!STRING:0"].szValue); // Check and return result return (hs->szCdnServers && hs->szCdnPath) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } } } return ERROR_FILE_NOT_FOUND; } static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv) { PFNPRODUCTCALLBACK PfnProductCallback = hs->pArgs->PfnProductCallback; LPCSTR szProductColumn = "Product!STRING:0"; LPCSTR szCodeName; void * PtrProductParam = hs->pArgs->PtrProductParam; size_t nProductColumnIndex = Csv.GetColumnIndex(szProductColumn); size_t nLineCount = Csv.GetLineCount(); size_t nSelected = CASC_INVALID_INDEX; DWORD dwErrCode; // // Find the configuration that we're gonna load. // // If the product is not specified and there is product callback, we use the callback to specify the product if(hs->szCodeName == NULL && nProductColumnIndex != CASC_INVALID_INDEX && PfnProductCallback != NULL) { LPCSTR ProductsList[0x40] = {NULL}; size_t nProductCount = 0; size_t nChoiceIndex = CASC_INVALID_INDEX; size_t nDefault = CASC_INVALID_INDEX; // Load all products to the array for(size_t i = 0; i < nLineCount; i++) { // Ignore anything that is not marked "active" if(!strcmp(Csv[i]["Active!DEC:1"].szValue, "1")) { ProductsList[nProductCount] = Csv[i]["Product!STRING:0"].szValue; nProductCount++; nDefault = i; } } // Only if there is more than one active products if(nProductCount > 1) { // Ask the callback to choose the product if(!PfnProductCallback(PtrProductParam, ProductsList, nProductCount, &nChoiceIndex) || (nChoiceIndex >= nProductCount)) return ERROR_CANCELLED; // We now have preferred product to open SetProductCodeName(hs, ProductsList[nChoiceIndex]); } else if(nProductCount == 1) { // We now have preferred product to open SetProductCodeName(hs, ProductsList[nDefault]); } else { return ERROR_FILE_NOT_FOUND; } } // Parse all lines in the CSV file. Either take the first that is active // or take the one that has been selected by the callback for(size_t i = 0; i < nLineCount; i++) { // Ignore anything that is not marked "active" if(!strcmp(Csv[i]["Active!DEC:1"].szValue, "1")) { // If the product code is specified and exists in the build file, we need to check it if(hs->szCodeName != NULL && (szCodeName = Csv[i]["Product!STRING:0"].szValue) != NULL) { TCHAR szBuffer[0x20]; // Skip if they are not equal CascStrCopy(szBuffer, _countof(szBuffer), szCodeName); if(_tcsicmp(hs->szCodeName, szBuffer)) continue; // Save the code name of the selected product SetProductCodeName(hs, Csv[i]["Product!STRING:0"].szValue); nSelected = i; break; } // If the caller didn't specify the product code name, pick the first active nSelected = i; break; } } // Load the selected product if(nSelected < nLineCount) { // Extract the CDN build key dwErrCode = LoadQueryKey(Csv[nSelected]["Build Key!HEX:16"], hs->CdnBuildKey); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Extract the CDN config key dwErrCode = LoadQueryKey(Csv[nSelected]["CDN Key!HEX:16"], hs->CdnConfigKey); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Get the CDN path GetDefaultCdnPath(hs, Csv[nSelected]["CDN Path!STRING:0"]); // If we found tags, we can extract language build from it GetDefaultLocaleMask(hs, Csv[nSelected]["Tags!STRING:0"]); // If we found version, extract a build number const CASC_CSV_COLUMN & VerColumn = Csv[nSelected]["Version!STRING:0"]; if(VerColumn.szValue && VerColumn.nLength) { LoadBuildNumber(hs, NULL, VerColumn.szValue, VerColumn.szValue + VerColumn.nLength, NULL); } // Verify all variables return (hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } return ERROR_FILE_NOT_FOUND; } static DWORD ParseFile_VersionsDb(TCascStorage * hs, CASC_CSV & Csv) { size_t nLineCount = Csv.GetLineCount(); DWORD dwErrCode = ERROR_SUCCESS; // Find the active config for (size_t i = 0; i < nLineCount; i++) { // Either take the version required or take the first one if(hs->szRegion == NULL || !strcmp(Csv[i]["Region!STRING:0"].szValue, hs->szRegion)) { // Extract the CDN build key dwErrCode = LoadQueryKey(Csv[i]["BuildConfig!HEX:16"], hs->CdnBuildKey); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Extract the CDN config key dwErrCode = LoadQueryKey(Csv[i]["CDNConfig!HEX:16"], hs->CdnConfigKey); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; const CASC_CSV_COLUMN & VerColumn = Csv[i]["VersionsName!String:0"]; if(VerColumn.szValue && VerColumn.nLength) { LoadBuildNumber(hs, NULL, VerColumn.szValue, VerColumn.szValue + VerColumn.nLength, NULL); } // Verify all variables if(hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) { // If we have manually given build key, override the value if(hs->szBuildKey != NULL) dwErrCode = BinaryFromString(hs->szBuildKey, MD5_STRING_SIZE, hs->CdnBuildKey.pbData); return dwErrCode; } return ERROR_BAD_FORMAT; } } return ERROR_FILE_NOT_FOUND; } static DWORD ParseFile_BuildDb(TCascStorage * hs, CASC_CSV & Csv) { DWORD dwErrCode; // Extract the CDN build key dwErrCode = LoadQueryKey(Csv[CSV_ZERO][CSV_ZERO], hs->CdnBuildKey); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Extract the CDN config key dwErrCode = LoadQueryKey(Csv[CSV_ZERO][1], hs->CdnConfigKey); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Verify all variables return (hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } static DWORD ParseFile_CdnConfig(TCascStorage * hs, void * pvListFile) { const char * szLineBegin; const char * szLineEnd; DWORD dwErrCode = ERROR_SUCCESS; // Keep parsing the listfile while there is something in there for(;;) { // Get the next line if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd)) break; // CDN key of ARCHIVE-GROUP. Archive-group is actually a very special '.index' file. // It is essentially a merger of all .index files, with a structure change // When ".index" added after the ARCHIVE-GROUP, we get file name in "indices" folder if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "archive-group", LoadQueryKey, &hs->ArchiveGroup)) continue; // CDN key of all archives. When ".index" added to the string, we get file name in "indices" folder if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "archives", LoadQueryKey, &hs->ArchivesKey)) continue; // CDN keys of patch archives (needs research) if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch-archives", LoadQueryKey, &hs->PatchArchivesKey)) continue; // CDN key of probably the combined patch index file (needs research) if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch-archive-group", LoadQueryKey, &hs->PatchArchivesGroup)) continue; // List of build configs this config supports (optional) // Points to file: "data\config\%02X\%02X\%s if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "builds", LoadQueryKey, &hs->BuildFiles)) continue; } // Check if all required fields are present if(hs->ArchivesKey.pbData == NULL || hs->ArchivesKey.cbData == 0) return ERROR_BAD_FORMAT; return dwErrCode; } static DWORD ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile) { const char * szLineBegin; const char * szLineEnd = NULL; DWORD dwErrCode; // Initialize the empty VFS array dwErrCode = hs->VfsRootList.Create(0x10); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Parse all variables while(ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd) != 0) { // Product name and build name if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "build-uid", LoadBuildProductId, NULL)) continue; if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "build-name", LoadBuildNumber, NULL)) continue; // Content key of the ROOT file. Look this up in ENCODING to get the encoded key if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "root*", LoadCKeyEntry, &hs->RootFile)) continue; // Content key [+ encoded key] of the INSTALL file // If EKey is absent, you need to query the ENCODING file for it if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "install*", LoadCKeyEntry, &hs->InstallCKey)) continue; // Content key [+ encoded key] of the DOWNLOAD file // If EKey is absent, you need to query the ENCODING file for it if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "download*", LoadCKeyEntry, &hs->DownloadCKey)) continue; // Content key + encoded key of the ENCODING file. Contains CKey+EKey // If either none or 1 is found, the game (at least Wow) switches to plain-data(?). Seen in build 20173 if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "encoding*", LoadCKeyEntry, &hs->EncodingCKey)) continue; // PATCH file if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch*", LoadEKeyEntry, &hs->PatchFile)) continue; // SIZE file if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "size*", LoadCKeyEntry, &hs->SizeFile)) continue; // VFS root file (the root file of the storage VFS) if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "vfs-root*", LoadCKeyEntry, &hs->VfsRoot)) continue; // Load a directory entry to the VFS if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "vfs-*", LoadVfsRootEntry, &hs->VfsRootList)) continue; } // Special case: Some builds of WoW (22267) don't have the variable in the build file if(hs->szCodeName == NULL && hs->szCdnPath != NULL) { hs->szCodeName = CascNewStr(GetPlainFileName(hs->szCdnPath)); } // Both CKey and EKey of ENCODING file is required if((hs->EncodingCKey.Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY)) != (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY)) dwErrCode = ERROR_BAD_FORMAT; return dwErrCode; } static DWORD CheckDataDirectory(TCascStorage * hs, LPTSTR szDirectory) { TCHAR szDataPath[MAX_PATH]; DWORD dwErrCode = ERROR_FILE_NOT_FOUND; // Try all known subdirectories for(size_t i = 0; DataDirs[i] != NULL; i++) { // Create the eventual data path CombinePath(szDataPath, _countof(szDataPath), szDirectory, DataDirs[i], NULL); // Does that directory exist? if(DirectoryExists(szDataPath)) { hs->szRootPath = CascNewStr(szDirectory); hs->szDataPath = CascNewStr(szDataPath); return ERROR_SUCCESS; } } return dwErrCode; } static DWORD LoadCsvFile(TCascStorage * hs, LPBYTE pbFileData, size_t cbFileData, PARSECSVFILE PfnParseProc, bool bHasHeader) { CASC_CSV Csv(0x40, bHasHeader); DWORD dwErrCode; // Load the external file to memory if((dwErrCode = Csv.Load(pbFileData, cbFileData)) == ERROR_SUCCESS) dwErrCode = PfnParseProc(hs, Csv); return dwErrCode; } static DWORD LoadCsvFile(TCascStorage * hs, LPCTSTR szFileName, PARSECSVFILE PfnParseProc, bool bHasHeader) { CASC_CSV Csv(0x40, bHasHeader); DWORD dwErrCode; // Load the external file to memory if((dwErrCode = Csv.Load(szFileName)) == ERROR_SUCCESS) dwErrCode = PfnParseProc(hs, Csv); return dwErrCode; } static LPCTSTR ExtractCdnServerName(LPTSTR szServerName, size_t cchServerName, LPCTSTR szCdnServers) { LPCTSTR szSeparator; if(szCdnServers[0] != 0) { szSeparator = _tcschr(szCdnServers, _T(' ')); if(szSeparator != NULL) { // Copy one server name CascStrCopy(szServerName, cchServerName, szCdnServers, (szSeparator - szCdnServers)); // Skip all separators while(szSeparator[0] == ' ' || szSeparator[0] == 0x09) szSeparator++; return szSeparator; } else { CascStrCopy(szServerName, MAX_PATH, szCdnServers); return szCdnServers + _tcslen(szCdnServers); } } return NULL; } static void CreateRemoteAndLocalPath(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo, CASC_PATH & RemotePath, CASC_PATH & LocalPath) { PCASC_EKEY_ENTRY pEKeyEntry; ULONGLONG ByteMask = 1; DWORD ArchiveIndex; // Append the CDN host / root folder RemotePath.SetPathRoot(CdnsInfo.szCdnsHost); RemotePath.AppendString(CdnsInfo.szCdnsPath, true); LocalPath.SetPathRoot(hs->szRootPath); // If there is an EKey, take EKey if(CdnsInfo.pbEKey != NULL) { // The file is given by EKey. It's either a loose file, or it's stored in an archive. // We check that using the EKey map if((pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexMap.FindObject(CdnsInfo.pbEKey)) != NULL) { // Change the path type to "data" RemotePath.AppendString(_T("data"), true); LocalPath.AppendString(_T("data"), true); // Append the EKey of the archive instead of the file itself ArchiveIndex = (DWORD)(pEKeyEntry->StorageOffset >> hs->FileOffsetBits); CdnsInfo.pbArchiveKey = hs->ArchivesKey.pbData + (MD5_HASH_SIZE * ArchiveIndex); RemotePath.AppendEKey(CdnsInfo.pbArchiveKey); LocalPath.AppendEKey(CdnsInfo.pbArchiveKey); // Get the archive index and archive offset CdnsInfo.ArchiveIndex = ArchiveIndex; CdnsInfo.ArchiveOffs = pEKeyEntry->StorageOffset & ((ByteMask << hs->FileOffsetBits) - 1); CdnsInfo.EncodedSize = pEKeyEntry->EncodedSize; } else { // Append the path type RemotePath.AppendString(CdnsInfo.szPathType, true); LocalPath.AppendString((CdnsInfo.szLoPaType != NULL) ? CdnsInfo.szLoPaType : CdnsInfo.szPathType, true); // Append the EKey RemotePath.AppendEKey(CdnsInfo.pbEKey); LocalPath.AppendEKey(CdnsInfo.pbEKey); } } else { assert(CdnsInfo.szFileName != NULL); RemotePath.AppendString(CdnsInfo.szFileName, true); LocalPath.AppendString(CdnsInfo.szFileName, true); } // Append extension RemotePath.AppendString(CdnsInfo.szExtension, false); LocalPath.AppendString(CdnsInfo.szExtension, false); } static DWORD ForcePathExist(LPCTSTR szFileName, bool bIsFileName) { LPTSTR szLocalPath; size_t nIndex; bool bFirstSeparator = false; DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Sanity checks if(szFileName && szFileName[0]) { szLocalPath = CascNewStr(szFileName); if(szLocalPath != NULL) { // Get the end of search if(bIsFileName) CutLastPathPart(szLocalPath); // Check the entire path if(_taccess(szLocalPath, 0) != 0) { // Searth the entire path for(nIndex = 0; szLocalPath[nIndex] != 0; nIndex++) { if(szLocalPath[nIndex] == '\\' || szLocalPath[nIndex] == '/') { // Cut the path and verify whether the folder/file exists szLocalPath[nIndex] = 0; // Skip the very first separator if(bFirstSeparator == true) { // Is it there? if(DirectoryExists(szLocalPath) == false && MakeDirectory(szLocalPath) == false) { dwErrCode = ERROR_PATH_NOT_FOUND; break; } } // Restore the character szLocalPath[nIndex] = PATH_SEP_CHAR; bFirstSeparator = true; dwErrCode = ERROR_SUCCESS; } } // Now check the final path if(DirectoryExists(szLocalPath) || MakeDirectory(szLocalPath)) { dwErrCode = ERROR_SUCCESS; } } else { dwErrCode = ERROR_SUCCESS; } CASC_FREE(szLocalPath); } } return dwErrCode; } static bool FileAlreadyExists(LPCTSTR szFileName) { TFileStream * pStream; ULONGLONG FileSize = 0; // The file open must succeed and also must be of non-zero size if((pStream = FileStream_OpenFile(szFileName, 0)) != NULL) { FileStream_GetSize(pStream, &FileSize); FileStream_Close(pStream); } return (FileSize != 0); } static DWORD DownloadFile( LPCTSTR szRemoteName, LPCTSTR szLocalName, PULONGLONG PtrByteOffset, DWORD cbReadSize, DWORD dwPortFlags) { TFileStream * pRemStream; TFileStream * pLocStream; LPBYTE pbFileData; DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Open the remote stream pRemStream = FileStream_OpenFile(szRemoteName, BASE_PROVIDER_HTTP | STREAM_PROVIDER_FLAT | dwPortFlags); if(pRemStream != NULL) { // Will we download the entire file or just a part of it? if(PtrByteOffset == NULL) { ULONGLONG FileSize = 0; // Retrieve the file size, but not longer than 1 GB if(FileStream_GetSize(pRemStream, &FileSize) && 0 < FileSize && FileSize < 0x40000000) { // Cut the file size down to 32 bits cbReadSize = (DWORD)FileSize; } } // Shall we read something? if((cbReadSize != 0) && (pbFileData = CASC_ALLOC(cbReadSize)) != NULL) { // Read all required data from the remote file if(FileStream_Read(pRemStream, PtrByteOffset, pbFileData, cbReadSize)) { pLocStream = FileStream_CreateFile(szLocalName, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT); if(pLocStream != NULL) { if(FileStream_Write(pLocStream, NULL, pbFileData, cbReadSize)) dwErrCode = ERROR_SUCCESS; FileStream_Close(pLocStream); } } // Free the data buffer CASC_FREE(pbFileData); } // Close the remote stream FileStream_Close(pRemStream); } else { dwErrCode = GetCascError(); } return dwErrCode; } static DWORD RibbitDownloadFile(LPCTSTR szProduct, LPCTSTR szFileName, QUERY_KEY & FileData) { TFileStream * pStream; ULONGLONG FileSize = 0; TCHAR szRemoteUrl[256]; DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; // Construct the full URL (https://wowdev.wiki/Ribbit) // Old (HTTP) download: wget http://us.patch.battle.net:1119/wow_classic/cdns CascStrPrintf(szRemoteUrl, _countof(szRemoteUrl), _T("ribbit://%s.version.battle.net/v1/products/%s/%s"), bnet_region, szProduct, szFileName); // Open the file stream if((pStream = FileStream_OpenFile(szRemoteUrl, 0)) != NULL) { if(FileStream_GetSize(pStream, &FileSize) && FileSize <= 0x04000000) { // Fill-in the file pointer and size FileData.pbData = CASC_ALLOC((size_t)FileSize); if(FileData.pbData != NULL) { if(FileStream_Read(pStream, NULL, FileData.pbData, (DWORD)FileSize)) { FileData.cbData = (size_t)FileSize; dwErrCode = ERROR_SUCCESS; } else { dwErrCode = GetCascError(); CASC_FREE(FileData.pbData); FileData.pbData = NULL; } } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } else { dwErrCode = GetCascError(); } // Close the remote stream FileStream_Close(pStream); } else { dwErrCode = GetCascError(); } return dwErrCode; } static DWORD DownloadFileFromCDN2(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo) { CASC_PATH RemotePath(URL_SEP_CHAR); CASC_PATH LocalPath(PATH_SEP_CHAR); DWORD dwErrCode; // Assemble both the remote and local path assert(CdnsInfo.szCdnsHost != NULL && CdnsInfo.szCdnsHost[0] != 0); CreateRemoteAndLocalPath(hs, CdnsInfo, RemotePath, LocalPath); // Check whether the local file exists if((CdnsInfo.Flags & CASC_CDN_FORCE_DOWNLOAD) || !FileAlreadyExists(LocalPath)) { // Make sure that the path exists dwErrCode = ForcePathExist(LocalPath, true); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Attempt to download the file dwErrCode = DownloadFile(RemotePath, LocalPath, NULL, 0, 0); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; } // Give the path to the caller, if any LocalPath.Copy(CdnsInfo.szLocalPath, CdnsInfo.ccLocalPath); return ERROR_SUCCESS; } DWORD DownloadFileFromCDN(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo) { LPCTSTR szCdnServers = hs->szCdnServers; TCHAR szCdnHost[MAX_PATH] = _T(""); DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; // If we have a given CDN server, use it. If not, try all CDNs // from the storage's configuration if(CdnsInfo.szCdnsHost == NULL) { // Try all download servers while((szCdnServers = ExtractCdnServerName(szCdnHost, _countof(szCdnHost), szCdnServers)) != NULL) { CdnsInfo.szCdnsHost = szCdnHost; if((dwErrCode = DownloadFileFromCDN2(hs, CdnsInfo)) == ERROR_SUCCESS) return ERROR_SUCCESS; } } else { dwErrCode = DownloadFileFromCDN2(hs, CdnsInfo); } return dwErrCode; } static DWORD FetchAndLoadConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PARSETEXTFILE PfnParseProc) { LPCTSTR szPathType = _T("config"); TCHAR szLocalPath[MAX_PATH]; TCHAR szSubDir[0x80] = _T(""); void * pvListFile = NULL; DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; // If online storage, we download the file. Otherwise, create a local path if(hs->dwFeatures & CASC_FEATURE_ONLINE) { CASC_CDN_DOWNLOAD CdnsInfo = {0}; // Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file CdnsInfo.szCdnsPath = hs->szCdnPath; CdnsInfo.szPathType = szPathType; CdnsInfo.pbEKey = pFileKey->pbData; CdnsInfo.szLocalPath = szLocalPath; CdnsInfo.ccLocalPath = _countof(szLocalPath); // Download the config file dwErrCode = DownloadFileFromCDN(hs, CdnsInfo); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; } else { CASC_PATH Path; Path.AppendString(hs->szDataPath, false); Path.AppendString(szSubDir, true); Path.AppendString(szPathType, true); Path.AppendEKey(pFileKey->pbData); Path.Copy(szLocalPath, _countof(szLocalPath)); } // Load and verify the external listfile pvListFile = ListFile_OpenExternal(szLocalPath); if(pvListFile != NULL) { if(ListFile_VerifyMD5(pvListFile, pFileKey->pbData)) { dwErrCode = PfnParseProc(hs, pvListFile); } else { dwErrCode = ERROR_FILE_CORRUPT; } CASC_FREE(pvListFile); } else { dwErrCode = ERROR_FILE_NOT_FOUND; } return dwErrCode; } //----------------------------------------------------------------------------- // Public functions bool InvokeProgressCallback(TCascStorage * hs, LPCSTR szMessage, LPCSTR szObject, DWORD CurrentValue, DWORD TotalValue) { PCASC_OPEN_STORAGE_ARGS pArgs = hs->pArgs; bool bResult = false; if(pArgs && pArgs->PfnProgressCallback) bResult = pArgs->PfnProgressCallback(pArgs->PtrProgressParam, szMessage, szObject, CurrentValue, TotalValue); return bResult; } DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PULONGLONG PtrEncodedSize) { ULONGLONG ContentSize = 0; ULONGLONG EncodedSize = 0; DWORD dwSpanCount = pCKeyEntry->SpanCount; bool bContentSizeError = false; bool bEncodedSizeError = false; // Sanity check assert(pCKeyEntry->SpanCount != 0); // Sum the file size over all file spans // Note: The first file span, if referenced by the ROOT folder, gets the same size // like the entire file (example: zone\base.xpak, zone\base.xpak_1) for(DWORD i = 0; i < dwSpanCount; i++, pCKeyEntry++) { if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE) bContentSizeError = true; if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE) bEncodedSizeError = true; ContentSize = ContentSize + pCKeyEntry->ContentSize; EncodedSize = EncodedSize + pCKeyEntry->EncodedSize; } // Reset the sizes if there was an error PtrContentSize[0] = (bContentSizeError == false) ? ContentSize : CASC_INVALID_SIZE64; PtrEncodedSize[0] = (bEncodedSizeError == false) ? EncodedSize : CASC_INVALID_SIZE64; return dwSpanCount; } // Checks whether there is a ".build.info" or ".build.db". // If yes, the function sets "szDataPath" and "szIndexPath" // in the storage structure and returns ERROR_SUCCESS DWORD CheckGameDirectory(TCascStorage * hs, LPTSTR szDirectory) { TFileStream * pStream; TCHAR szBuildFile[MAX_PATH]; DWORD dwErrCode = ERROR_FILE_NOT_FOUND; // Try to find any of the root files used in the history for (size_t i = 0; BuildTypes[i].szFileName != NULL; i++) { // Create the full name of the .agent.db file CombinePath(szBuildFile, _countof(szBuildFile), szDirectory, BuildTypes[i].szFileName, NULL); // Attempt to open the file pStream = FileStream_OpenFile(szBuildFile, STREAM_FLAG_READ_ONLY); if(pStream != NULL) { // Free the stream FileStream_Close(pStream); // Check for the data directory dwErrCode = CheckDataDirectory(hs, szDirectory); if(dwErrCode == ERROR_SUCCESS) { hs->szBuildFile = CascNewStr(szBuildFile); hs->BuildFileType = BuildTypes[i].BuildFileType; return ERROR_SUCCESS; } } } return dwErrCode; } DWORD LoadBuildInfo(TCascStorage * hs) { PARSECSVFILE PfnParseProc = NULL; DWORD dwErrCode; bool bHasHeader = true; // We support ".build.info", ".build.db" or "versions" switch (hs->BuildFileType) { case CascBuildInfo: PfnParseProc = ParseFile_BuildInfo; break; case CascVersionsDb: PfnParseProc = ParseFile_VersionsDb; break; case CascBuildDb: PfnParseProc = ParseFile_BuildDb; bHasHeader = false; break; default: return ERROR_NOT_SUPPORTED; } // If the storage is online storage, we need to download "versions" if(hs->dwFeatures & CASC_FEATURE_ONLINE) { QUERY_KEY FileData; // Inform the user about loading the build.info/build.db/versions if(InvokeProgressCallback(hs, "Downloading the \"versions\" file", NULL, 0, 0)) return ERROR_CANCELLED; // Download the file using Ribbit protocol dwErrCode = RibbitDownloadFile(hs->szCodeName, _T("versions"), FileData); if(dwErrCode == ERROR_SUCCESS) { // Parse the downloaded file dwErrCode = LoadCsvFile(hs, FileData.pbData, FileData.cbData, ParseFile_VersionsDb, true); } } else { assert(hs->szBuildFile != NULL); dwErrCode = LoadCsvFile(hs, hs->szBuildFile, PfnParseProc, bHasHeader); } return dwErrCode; } DWORD LoadCdnsFile(TCascStorage * hs) { QUERY_KEY FileData; DWORD dwErrCode = ERROR_SUCCESS; // Sanity checks assert(hs->dwFeatures & CASC_FEATURE_ONLINE); // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Downloading the \"cdns\" file", NULL, 0, 0)) return ERROR_CANCELLED; // Download the file using Ribbit protocol dwErrCode = RibbitDownloadFile(hs->szCodeName, _T("cdns"), FileData); if(dwErrCode == ERROR_SUCCESS) { // Parse the downloaded file dwErrCode = LoadCsvFile(hs, FileData.pbData, FileData.cbData, ParseFile_CDNS, true); } return dwErrCode; } DWORD LoadCdnConfigFile(TCascStorage * hs) { // The CKey for the CDN config should have been loaded already assert(hs->CdnConfigKey.pbData != NULL && hs->CdnConfigKey.cbData == MD5_HASH_SIZE); // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Loading CDN config file", NULL, 0, 0)) return ERROR_CANCELLED; // Load the CDN config file return FetchAndLoadConfigFile(hs, &hs->CdnConfigKey, ParseFile_CdnConfig); } DWORD LoadCdnBuildFile(TCascStorage * hs) { // The CKey for the CDN config should have been loaded already assert(hs->CdnBuildKey.pbData != NULL && hs->CdnBuildKey.cbData == MD5_HASH_SIZE); // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Loading CDN build file", NULL, 0, 0)) return ERROR_CANCELLED; // Load the CDN config file. Note that we don't // need it for anything, really, so we don't care if it fails return FetchAndLoadConfigFile(hs, &hs->CdnBuildKey, ParseFile_CdnBuild); } LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData) { LPBYTE pbFileData = NULL; HANDLE hFile = NULL; DWORD dwFileSizeHi = 0; DWORD cbFileData = 0; DWORD dwBytesRead = 0; DWORD dwErrCode = ERROR_SUCCESS; // Open the file either by CKey or by EKey if(OpenFileByCKeyEntry(hs, pCKeyEntry, CASC_STRICT_DATA_CHECK, &hFile)) { // Make the file not cached. We always load the entire file to memory, // so no need to cache (and needlessly copy from one buffer to another) SetCacheStrategy(hFile, CascCacheNothing); // Retrieve the size of the file. Note that the caller might specify // the real size of the file, in case the file size is not retrievable // or if the size is wrong. Example: ENCODING file has size specified in BUILD if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE) { cbFileData = CascGetFileSize(hFile, &dwFileSizeHi); if(cbFileData == CASC_INVALID_SIZE || dwFileSizeHi != 0) dwErrCode = ERROR_FILE_CORRUPT; } else { cbFileData = pCKeyEntry->ContentSize; } // Load the entire file to memory if(dwErrCode == ERROR_SUCCESS) { // Allocate space for the ENCODING file pbFileData = CASC_ALLOC(cbFileData); if(pbFileData != NULL) { // Read the entire file to memory CascReadFile(hFile, pbFileData, cbFileData, &dwBytesRead); if(dwBytesRead != cbFileData) { dwErrCode = ERROR_FILE_CORRUPT; } } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } // Close the file CascCloseFile(hFile); } else { dwErrCode = GetCascError(); } // Handle errors if(dwErrCode != ERROR_SUCCESS) { // Free the file data CASC_FREE(pbFileData); cbFileData = 0; // Set the last error SetCascError(dwErrCode); } // Give the loaded file length if(pcbFileData != NULL) *pcbFileData = cbFileData; return pbFileData; } LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData) { TFileStream * pStream; ULONGLONG FileSize = 0; LPBYTE pbFileData = NULL; DWORD cbFileData = 0; // Open the stream for read-only access and read the file pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); if(pStream != NULL) { // Retrieve the file size FileStream_GetSize(pStream, &FileSize); cbFileData = (DWORD)FileSize; // Do not load zero files or too large files if(0 < FileSize && FileSize <= 0x2000000) { // Allocate file data buffer. Make it 1 byte longer // so string functions can put '\0' there pbFileData = CASC_ALLOC(cbFileData + 1); if(pbFileData != NULL) { if(FileStream_Read(pStream, NULL, pbFileData, cbFileData)) { // Terminate the data with zero so various string-based functions can process it pbFileData[cbFileData] = 0; } else { CASC_FREE(pbFileData); cbFileData = 0; } } else { SetCascError(ERROR_NOT_ENOUGH_MEMORY); cbFileData = 0; } } else { SetCascError(ERROR_BAD_FORMAT); cbFileData = 0; assert(false); } // Close the file stream FileStream_Close(pStream); } // Give out values if(pcbFileData != NULL) pcbFileData[0] = cbFileData; return pbFileData; } ================================================ FILE: deps/CascLib/src/CascFindFile.cpp ================================================ /*****************************************************************************/ /* CascFindFile.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* System-dependent directory functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 10.05.14 1.00 Lad The first version of CascFindFile.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Local functions // Reset the search structure. Called before each search static void ResetFindData(PCASC_FIND_DATA pFindData) { // Reset the variables ZeroMemory16(pFindData->CKey); ZeroMemory16(pFindData->EKey); pFindData->szFileName[0] = 0; pFindData->szPlainName = pFindData->szFileName; pFindData->TagBitMask = 0; pFindData->FileSize = CASC_INVALID_SIZE64; pFindData->dwFileDataId = CASC_INVALID_ID; pFindData->dwLocaleFlags = CASC_INVALID_ID; pFindData->dwContentFlags = CASC_INVALID_ID; pFindData->dwSpanCount = 1; pFindData->NameType = CascNameFull; pFindData->bFileAvailable = false; } static void SupplyFakeFileName(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry) { // If there is a file data ID, create fake file name if(pFindData->dwFileDataId != CASC_INVALID_ID) { CascStrPrintf(pFindData->szFileName, _countof(pFindData->szFileName), "FILE%08X.dat", pFindData->dwFileDataId); pFindData->NameType = CascNameDataId; return; } // If there is a CKey, convert the CKey to file name if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY) { StringFromBinary(pFindData->CKey, MD5_HASH_SIZE, pFindData->szFileName); pFindData->NameType = CascNameCKey; return; } // EKey should be always present if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY) { StringFromBinary(pFindData->EKey, MD5_HASH_SIZE, pFindData->szFileName); pFindData->NameType = CascNameEKey; return; } assert(false); } static bool CopyCKeyEntryToFindData(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry) { ULONGLONG ContentSize = 0; ULONGLONG EncodedSize = 0; // Supply both keys CopyMemory16(pFindData->CKey, pCKeyEntry->CKey); CopyMemory16(pFindData->EKey, pCKeyEntry->EKey); // Supply the tag mask pFindData->TagBitMask = pCKeyEntry->TagBitMask; // Supply the plain name. Only do that if the found name is not a CKey/EKey if(pFindData->szFileName[0] != 0) pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName); // If we retrieved the file size directly from the root provider, use it // Otherwise, supply EncodedSize or ContentSize, whichever is available (but ContentSize > EncodedSize) pFindData->dwSpanCount = GetFileSpanInfo(pCKeyEntry, &ContentSize, &EncodedSize); if(pFindData->FileSize == CASC_INVALID_SIZE64) { if(ContentSize != CASC_INVALID_SIZE64) pFindData->FileSize = ContentSize; else pFindData->FileSize = EncodedSize; } // Set flag indicating that the file is locally available pFindData->bFileAvailable = (pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL); // Supply a fake file name, if there is none supplied by the root handler if(pFindData->szFileName[0] == 0) SupplyFakeFileName(pFindData, pCKeyEntry); return true; } // Perform searching using root-specific provider. // The provider may need the listfile static bool DoStorageSearch_RootFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) { PCASC_CKEY_ENTRY pCKeyEntry; TCascStorage * hs = pSearch->hs; // Reset the search structure ResetFindData(pFindData); // Enumerate over all files for(;;) { // Attempt to find (the next) file from the root handler pCKeyEntry = hs->pRootHandler->Search(pSearch, pFindData); if(pCKeyEntry == NULL) return false; // The entry is expected to be referenced by the root directory assert(pCKeyEntry->RefCount != 0); // Copy the CKey entry to the find data and return it return CopyCKeyEntryToFindData(pFindData, pCKeyEntry); } } static bool DoStorageSearch_CKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) { PCASC_CKEY_ENTRY pCKeyEntry; TCascStorage * hs = pSearch->hs; size_t nTotalItems = hs->CKeyArray.ItemCount(); // Reset the find data structure ResetFindData(pFindData); // Check for CKeys that haven't been found yet while(pSearch->nFileIndex < nTotalItems) { // Locate the n-th CKey entry. pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(pSearch->nFileIndex++); //BREAK_ON_XKEY3(pCKeyEntry->CKey, 0x2B, 0xfc, 0xe4); // Only report files that are unreferenced by the ROOT handler if(pCKeyEntry->IsFile() && pCKeyEntry->RefCount == 0) { return CopyCKeyEntryToFindData(pFindData, pCKeyEntry); } } // Nameless search ended return false; } static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) { // State 0: No search done yet if(pSearch->nSearchState == 0) { // Does the search specify listfile? if(pSearch->szListFile != NULL) pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile); // Move the search phase to the listfile searching pSearch->nSearchState = 1; pSearch->nFileIndex = 0; } // State 1: Searching the list file if(pSearch->nSearchState == 1) { if(DoStorageSearch_RootFile(pSearch, pFindData)) return true; // Move to the nameless search state pSearch->nSearchState = 2; pSearch->nFileIndex = 0; } // State 2: Searching the remaining entries by CKey if(pSearch->nSearchState == 2 && (pSearch->szMask == NULL || !strcmp(pSearch->szMask, "*"))) { if(DoStorageSearch_CKey(pSearch, pFindData)) return true; // Move to the final search state pSearch->nSearchState++; pSearch->nFileIndex = 0; } return false; } //----------------------------------------------------------------------------- // Public functions HANDLE WINAPI CascFindFirstFile( HANDLE hStorage, LPCSTR szMask, PCASC_FIND_DATA pFindData, LPCTSTR szListFile) { TCascStorage * hs; TCascSearch * pSearch = NULL; DWORD dwErrCode = ERROR_SUCCESS; // Check parameters if((hs = TCascStorage::IsValid(hStorage)) == NULL) dwErrCode = ERROR_INVALID_HANDLE; if(pFindData == NULL) dwErrCode = ERROR_INVALID_PARAMETER; // Supply default mask, if needed if(szMask == NULL || szMask[0] == 0) szMask = "*"; // Init the search structure and search handle if(dwErrCode == ERROR_SUCCESS) { // Allocate the search handle pSearch = new TCascSearch(hs, szListFile, szMask); if(pSearch == NULL) dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Perform search if(dwErrCode == ERROR_SUCCESS) { if(!DoStorageSearch(pSearch, pFindData)) dwErrCode = ERROR_NO_MORE_FILES; } if(dwErrCode != ERROR_SUCCESS) { delete pSearch; pSearch = (TCascSearch *)INVALID_HANDLE_VALUE; } return (HANDLE)pSearch; } bool WINAPI CascFindNextFile( HANDLE hFind, PCASC_FIND_DATA pFindData) { TCascSearch * pSearch; pSearch = TCascSearch::IsValid(hFind); if(pSearch == NULL || pFindData == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Perform search return DoStorageSearch(pSearch, pFindData); } bool WINAPI CascFindClose(HANDLE hFind) { TCascSearch * pSearch; pSearch = TCascSearch::IsValid(hFind); if(pSearch == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } delete pSearch; return true; } ================================================ FILE: deps/CascLib/src/CascIndexFiles.cpp ================================================ /*****************************************************************************/ /* CascIndexFiles.cpp Copyright (c) Ladislav Zezula 2019 */ /*---------------------------------------------------------------------------*/ /* Index file support */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 23.05.19 1.00 Lad Created */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Local variables static LPCTSTR szAllowedHexChars = _T("0123456789aAbBcCdDeEfF"); static LPCTSTR szIndexFormat_V1 = _T("data.i%x%x"); static LPCTSTR szIndexFormat_V2 = _T("%02x%08x.idx"); // Limit for "orphaned" items - those that are in index files, but are not in ENCODING manifest #define CASC_MAX_ORPHANED_ITEMS 0x100 typedef bool (*EKEY_ENTRY_CALLBACK)(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry); //----------------------------------------------------------------------------- // Local functions // "data.iXY" static bool IsIndexFileName_V1(LPCTSTR szFileName) { // Check if the name looks like a valid index file return (_tcslen(szFileName) == 8 && _tcsnicmp(szFileName, _T("data.i"), 6) == 0 && _tcsspn(szFileName + 6, szAllowedHexChars) == 2); } static bool IsIndexFileName_V2(LPCTSTR szFileName) { // Check if the name looks like a valid index file return (_tcslen(szFileName) == 14 && _tcsspn(szFileName, _T("0123456789aAbBcCdDeEfF")) == 0x0A && _tcsicmp(szFileName + 0x0A, _T(".idx")) == 0); } static bool IndexDirectory_OnFileFound( LPCTSTR szFileName, void * pvContext) { TCascStorage * hs = (TCascStorage *)pvContext; PCASC_INDEX pIndexFile; DWORD IndexVersion = 0; DWORD IndexValue = 0; // Auto-detect the format of the index file name if(hs->szIndexFormat == NULL) { if(IsIndexFileName_V2(szFileName)) hs->szIndexFormat = szIndexFormat_V2; else if(IsIndexFileName_V1(szFileName)) hs->szIndexFormat = szIndexFormat_V1; else return false; } if(hs->szIndexFormat == szIndexFormat_V2) { // Check the index file name format if(!IsIndexFileName_V2(szFileName)) return false; // Get the main index from the first two digits if(ConvertStringToInt(szFileName + 0, 2, IndexValue) != ERROR_SUCCESS) return false; if(ConvertStringToInt(szFileName + 2, 8, IndexVersion) != ERROR_SUCCESS) return false; } else if(hs->szIndexFormat == szIndexFormat_V1) { // Check the index file name format if(!IsIndexFileName_V1(szFileName)) return false; // Get the main index from the first two digits if(ConvertStringToInt(szFileName + 6, 1, IndexValue) != ERROR_SUCCESS) return false; if(ConvertStringToInt(szFileName + 7, 1, IndexVersion) != ERROR_SUCCESS) return false; } else { // Should never happen assert(false); return false; } // The index value must not be greater than 0x0F if(IndexValue >= CASC_INDEX_COUNT) return false; pIndexFile = &hs->IndexFiles[IndexValue]; // If the new subindex is greater than the previous one, use this one instead if(IndexVersion > pIndexFile->NewSubIndex) { pIndexFile->OldSubIndex = pIndexFile->NewSubIndex; pIndexFile->NewSubIndex = IndexVersion; } else if(IndexVersion > pIndexFile->OldSubIndex) { pIndexFile->OldSubIndex = IndexVersion; } // Note: WoW6 only keeps last two index files // Any additional index files are deleted at this point return true; } static LPTSTR CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD IndexVersion) { TCHAR szFullName[MAX_PATH]; TCHAR szPlainName[0x40]; // Sanity checks assert(hs->szIndexFormat != NULL); assert(hs->szIndexPath != NULL); assert(IndexValue <= 0x0F); // Create the full path CascStrPrintf(szPlainName, _countof(szPlainName), hs->szIndexFormat, IndexValue, IndexVersion); CombinePath(szFullName, _countof(szFullName), hs->szIndexPath, szPlainName, NULL); // Return allocated path return CascNewStr(szFullName); } static void SaveFileOffsetBitsAndEKeyLength(TCascStorage * hs, BYTE FileOffsetBits, BYTE EKeyLength) { if(hs->FileOffsetBits == 0) hs->FileOffsetBits = FileOffsetBits; assert(hs->FileOffsetBits == FileOffsetBits); if(hs->EKeyLength == 0) hs->EKeyLength = EKeyLength; assert(hs->EKeyLength == EKeyLength); } // Verifies a guarded block - data availability and checksum match static LPBYTE CaptureGuardedBlock1(LPBYTE pbFileData, LPBYTE pbFileEnd) { PFILE_INDEX_GUARDED_BLOCK pBlock = (PFILE_INDEX_GUARDED_BLOCK)pbFileData; // Check the guarded block if((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK)) >= pbFileEnd) return NULL; if((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK) + pBlock->BlockSize) > pbFileEnd) return NULL; // Verify the hash if(hashlittle(pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK), pBlock->BlockSize, 0) != pBlock->BlockHash) return NULL; // Give the output return (LPBYTE)(pBlock + 1); } // Second method of checking a guarded block; hash is calculated entry-by-entry. // Unfortunately, due to implementation in hashlittle2(), we cannot calculate the hash of a continuous block static LPBYTE CaptureGuardedBlock2(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t EntryLength, PDWORD PtrBlockSize = NULL) { PFILE_INDEX_GUARDED_BLOCK pBlock = (PFILE_INDEX_GUARDED_BLOCK)pbFileData; LPBYTE pbEntryPtr; size_t EntryCount; unsigned int HashBlizzGet = 0; unsigned int HashHigh = 0; unsigned int HashLow = 0; // Check the guarded block. There must be enough bytes to contain FILE_INDEX_GUARDED_BLOCK // and also the block length must not be NULL if ((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK)) >= pbFileEnd) return NULL; if (pBlock->BlockSize == 0 || (pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK) + pBlock->BlockSize) > pbFileEnd) return NULL; // // Verify the hash from the Blizzard Downloader // pbEntryPtr = pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK); EntryCount = pBlock->BlockSize / EntryLength; for (size_t i = 0; i < EntryCount; i++) { hashlittle2(pbEntryPtr, EntryLength, &HashHigh, &HashLow); pbEntryPtr += EntryLength; } // Verify hash if (HashHigh == pBlock->BlockHash) { // Give the output if (PtrBlockSize != NULL) PtrBlockSize[0] = pBlock->BlockSize; return (LPBYTE)(pBlock + 1); } // // Verify the hash from the Blizzget tool, which calculates the hash differently // https://github.com/d07RiV/blizzget/blob/master/src/ngdp.cpp // Function void DataStorage::writeIndex() // pbEntryPtr = pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK); EntryCount = pBlock->BlockSize / EntryLength; for (size_t i = 0; i < EntryCount; i++) { HashBlizzGet = hashlittle(pbEntryPtr, EntryLength, HashBlizzGet); pbEntryPtr += EntryLength; } // Verify hash if (HashBlizzGet == pBlock->BlockHash) { // Give the output if (PtrBlockSize != NULL) PtrBlockSize[0] = pBlock->BlockSize; return (LPBYTE)(pBlock + 1); } // Hash mismatch return NULL; } // Third method of checking a guarded block; There is 32-bit hash, followed by EKey entry // The hash covers the EKey entry plus one byte static LPBYTE CaptureGuardedBlock3(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t EntryLength) { PDWORD PtrEntryHash = (PDWORD)pbFileData; DWORD EntryHash; // Check the guarded block. There must be enough bytes to contain single entry and the hash // Also, the hash must be present if ((pbFileData + sizeof(DWORD) + EntryLength) >= pbFileEnd) return NULL; if (PtrEntryHash[0] == 0) return NULL; EntryHash = hashlittle(pbFileData + sizeof(DWORD), EntryLength+1, 0) | 0x80000000; if(EntryHash != PtrEntryHash[0]) return NULL; // Give the output return (LPBYTE)(PtrEntryHash + 1); } /* static bool CaptureIndexEntry(CASC_INDEX_HEADER & InHeader, PCASC_EKEY_ENTRY pEKeyEntry, LPBYTE pbEKeyEntry) { // Copy the EKey of the variable length pbEKeyEntry = CaptureEncodedKey(pEKeyEntry->EKey, pbEKeyEntry, InHeader.EKeyLength); // Copy the storage offset and encoded size pEKeyEntry->StorageOffset = ConvertBytesToInteger_5(pbEKeyEntry); pEKeyEntry->EncodedSize = ConvertBytesToInteger_4_LE(pbEKeyEntry + InHeader.StorageOffsetLength); pEKeyEntry->Alignment = 0; // We ignore items that have EncodedSize of 0x1E return (pEKeyEntry->EncodedSize > FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature)); } static void InsertCKeyEntry(TCascStorage * hs, CASC_EKEY_ENTRY & EKeyEntry, DWORD Flags) { PCASC_CKEY_ENTRY pCKeyEntry; // Multiple items with the same EKey in the index files may exist. // Example: "2018 - New CASC\00001", EKey 37 89 16 5b 2d cc 71 c1 25 00 00 00 00 00 00 00 // Positions: 0x1D, 0x1E, 0x1F // In that case, we only take the first one into account // BREAK_ON_XKEY3(EKeyEntry.EKey, 0x09, 0xF3, 0xCD); // If the item is not there yet, insert a new one if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKeyEntry.EKey)) == NULL) { // Insert a new entry to the array. DO NOT ALLOW enlarge array here pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false); if(pCKeyEntry == NULL) return; // Fill-in the information ZeroMemory16(pCKeyEntry->CKey); CopyMemory16(pCKeyEntry->EKey, EKeyEntry.EKey); pCKeyEntry->StorageOffset = EKeyEntry.StorageOffset; pCKeyEntry->TagBitMask = 0; pCKeyEntry->ContentSize = CASC_INVALID_SIZE; pCKeyEntry->EncodedSize = EKeyEntry.EncodedSize; pCKeyEntry->Flags = CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL; pCKeyEntry->RefCount = 0; pCKeyEntry->SpanCount = 1; pCKeyEntry->Priority = 0; // Insert the item to the EKey table hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey); } else { // The entry already exists. True e.g. for ENCODING. // Only copy the storage offset and sizes if not available yet if(pCKeyEntry->StorageOffset == CASC_INVALID_OFFS64) { pCKeyEntry->StorageOffset = EKeyEntry.StorageOffset; pCKeyEntry->EncodedSize = EKeyEntry.EncodedSize; } } // Add the extra flag pCKeyEntry->Flags |= Flags; } */ static DWORD LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd) { size_t EntryLength = InHeader.EntryLength; while((pbEKeyEntry + EntryLength) <= pbEKeyEnd) { // ENCODING for Starcraft II Beta BREAK_ON_XKEY3(pbEKeyEntry, 0x8b, 0x0d, 0x9a); if(!PfnEKeyEntry(hs, InHeader, pbEKeyEntry)) return ERROR_INDEX_PARSING_DONE; pbEKeyEntry += EntryLength; } return ERROR_SUCCESS; } static DWORD CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData, DWORD BucketIndex) { PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData; LPBYTE pbKeyEntries; LPBYTE pbFileEnd = pbFileData + cbFileData; size_t cbKeyEntries; DWORD HeaderHash; // Check the available size. Note that the index file can be just a header. if((pbFileData + sizeof(FILE_INDEX_HEADER_V1)) > pbFileEnd) return ERROR_BAD_FORMAT; if(pIndexHeader->IndexVersion != 0x05 || pIndexHeader->BucketIndex != (BYTE)BucketIndex || pIndexHeader->field_8 == 0) return ERROR_BAD_FORMAT; if(pIndexHeader->EncodedSizeLength != 0x04 || pIndexHeader->StorageOffsetLength != 0x05 || pIndexHeader->EKeyLength != 0x09) return ERROR_NOT_SUPPORTED; // Verify the header hash HeaderHash = pIndexHeader->HeaderHash; pIndexHeader->HeaderHash = 0; if(hashlittle(pbFileData, sizeof(FILE_INDEX_HEADER_V1), 0) != HeaderHash) return ERROR_BAD_FORMAT; // Return the header hash back pIndexHeader->HeaderHash = HeaderHash; // Copy the fields InHeader.IndexVersion = pIndexHeader->IndexVersion; InHeader.BucketIndex = pIndexHeader->BucketIndex; InHeader.StorageOffsetLength = pIndexHeader->StorageOffsetLength; InHeader.EncodedSizeLength = pIndexHeader->EncodedSizeLength; InHeader.EKeyLength = pIndexHeader->EKeyLength; InHeader.FileOffsetBits = pIndexHeader->FileOffsetBits; InHeader.Alignment = 0; InHeader.SegmentSize = pIndexHeader->SegmentSize; // Determine the size of the header InHeader.HeaderLength = sizeof(FILE_INDEX_HEADER_V1); InHeader.HeaderPadding = 0; InHeader.EntryLength = pIndexHeader->EKeyLength + pIndexHeader->StorageOffsetLength + pIndexHeader->EncodedSizeLength; InHeader.EKeyCount = pIndexHeader->EKeyCount1 + pIndexHeader->EKeyCount2; // Verify the entries hash - 1st block pbKeyEntries = pbFileData + InHeader.HeaderLength; cbKeyEntries = pIndexHeader->EKeyCount1 * InHeader.EntryLength; if((pbKeyEntries + cbKeyEntries) > pbFileEnd) return ERROR_FILE_CORRUPT; if(hashlittle(pbKeyEntries, cbKeyEntries, 0) != pIndexHeader->KeysHash1) return ERROR_FILE_CORRUPT; // Verify the entries hash - 2nd block pbKeyEntries = pbKeyEntries + cbKeyEntries; cbKeyEntries = pIndexHeader->EKeyCount2 * InHeader.EntryLength; if((pbKeyEntries + cbKeyEntries) > pbFileEnd) return ERROR_FILE_CORRUPT; if(hashlittle(pbKeyEntries, cbKeyEntries, 0) != pIndexHeader->KeysHash2) return ERROR_FILE_CORRUPT; return ERROR_SUCCESS; } static DWORD CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData, DWORD BucketIndex) { PFILE_INDEX_HEADER_V2 pIndexHeader; LPBYTE pbFileEnd = pbFileData + cbFileData; // Check for guarded block if((pbFileData = CaptureGuardedBlock1(pbFileData, pbFileEnd)) == NULL) return ERROR_FILE_CORRUPT; pIndexHeader = (PFILE_INDEX_HEADER_V2)pbFileData; // Verify the content of the index header if(pIndexHeader->IndexVersion != 0x07 || pIndexHeader->BucketIndex != (BYTE)BucketIndex || pIndexHeader->ExtraBytes != 0x00) return ERROR_BAD_FORMAT; if(pIndexHeader->EncodedSizeLength != 0x04 || pIndexHeader->StorageOffsetLength != 0x05 || pIndexHeader->EKeyLength != 0x09) return ERROR_BAD_FORMAT; // Capture the values from the index header InHeader.IndexVersion = pIndexHeader->IndexVersion; InHeader.BucketIndex = pIndexHeader->BucketIndex; InHeader.StorageOffsetLength = pIndexHeader->StorageOffsetLength; InHeader.EncodedSizeLength = pIndexHeader->EncodedSizeLength; InHeader.EKeyLength = pIndexHeader->EKeyLength; InHeader.FileOffsetBits = pIndexHeader->FileOffsetBits; InHeader.Alignment = 0; InHeader.SegmentSize = pIndexHeader->SegmentSize; // Supply the lengths InHeader.HeaderLength = sizeof(FILE_INDEX_GUARDED_BLOCK) + sizeof(FILE_INDEX_HEADER_V2); InHeader.HeaderPadding = 8; InHeader.EntryLength = pIndexHeader->EKeyLength + pIndexHeader->StorageOffsetLength + pIndexHeader->EncodedSizeLength; InHeader.EKeyCount = 0; return ERROR_SUCCESS; } static DWORD LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbFileData, size_t cbFileData) { LPBYTE pbEKeyEntries = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding; // Remember the values from the index header SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength); // Load the entries from a continuous array return LoadIndexItems(hs, InHeader, PfnEKeyEntry, pbEKeyEntries, pbFileData + cbFileData); } static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbFileData, size_t cbFileData) { LPBYTE pbEKeyEntry; LPBYTE pbFileEnd = pbFileData + cbFileData; LPBYTE pbFilePtr = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding; size_t EKeyEntriesLength; DWORD BlockSize = 0; DWORD dwErrCode = ERROR_NOT_SUPPORTED; // Remember the values from the index header SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength); // Get the pointer to the first block of EKey entries if((pbEKeyEntry = CaptureGuardedBlock2(pbFilePtr, pbFileEnd, InHeader.EntryLength, &BlockSize)) != NULL) { // Supply the number of EKey entries InHeader.HeaderPadding += sizeof(FILE_INDEX_GUARDED_BLOCK); // Load the continuous array of EKeys return LoadIndexItems(hs, InHeader, PfnEKeyEntry, pbEKeyEntry, pbEKeyEntry + BlockSize); } // Get the pointer to the second block of EKey entries. // They are alway at the position aligned to 4096 EKeyEntriesLength = pbFileEnd - pbFilePtr; if(EKeyEntriesLength >= 0x7800) { LPBYTE pbStartPage = pbFileData + 0x1000; LPBYTE pbEndPage = pbStartPage + FILE_INDEX_PAGE_SIZE; size_t AlignedLength = ALIGN_TO_SIZE(InHeader.EntryLength, 4); // Parse the chunks with the EKey entries while(pbStartPage < pbFileEnd) { pbEKeyEntry = pbStartPage; while(pbEKeyEntry < pbEndPage) { // Check the EKey entry protected by 32-bit hash if((pbEKeyEntry = CaptureGuardedBlock3(pbEKeyEntry, pbEndPage, InHeader.EntryLength)) == NULL) break; // CASC\\0001: Encoding //BREAK_ON_XKEY3(pbEKeyEntry, 0xbc, 0xe8, 0x23); // Call the EKey entry callback if(!PfnEKeyEntry(hs, InHeader, pbEKeyEntry)) return ERROR_INDEX_PARSING_DONE; pbEKeyEntry += AlignedLength; } // Move to the next chunk pbStartPage += FILE_INDEX_PAGE_SIZE; } dwErrCode = ERROR_SUCCESS; } return dwErrCode; } static DWORD LoadIndexFile(TCascStorage * hs, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbFileData, size_t cbFileData, DWORD BucketIndex) { CASC_INDEX_HEADER InHeader; // Check for CASC version 2 if(CaptureIndexHeader_V2(InHeader, pbFileData, cbFileData, BucketIndex) == ERROR_SUCCESS) return LoadIndexFile_V2(hs, InHeader, PfnEKeyEntry, pbFileData, cbFileData); // Check for CASC index version 1 if(CaptureIndexHeader_V1(InHeader, pbFileData, cbFileData, BucketIndex) == ERROR_SUCCESS) return LoadIndexFile_V1(hs, InHeader, PfnEKeyEntry, pbFileData, cbFileData); // Should never happen assert(false); return ERROR_BAD_FORMAT; } // Checks the EKey entry for EKey of the ENCODING manifest static bool InsertEncodingEKeyToMap(TCascStorage * hs, CASC_INDEX_HEADER &, LPBYTE pbEKeyEntry) { hs->IndexEKeyMap.InsertObject(pbEKeyEntry, pbEKeyEntry); return true; } static DWORD ProcessLocalIndexFiles(TCascStorage * hs, EKEY_ENTRY_CALLBACK PfnEKeyEntry, DWORD dwIndexCount) { DWORD dwErrCode = ERROR_SUCCESS; // Load each index file for(DWORD i = 0; i < dwIndexCount; i++) { CASC_INDEX & IndexFile = hs->IndexFiles[i]; // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Loading index files", NULL, i, dwIndexCount)) { dwErrCode = ERROR_CANCELLED; break; } // Load the index file if((dwErrCode = LoadIndexFile(hs, PfnEKeyEntry, IndexFile.pbFileData, IndexFile.cbFileData, i)) != ERROR_SUCCESS) break; } // Swallow the "done parsing" error if(dwErrCode == ERROR_INDEX_PARSING_DONE) dwErrCode = ERROR_SUCCESS; // Remember the number of files that are present locally hs->LocalFiles = hs->CKeyArray.ItemCount(); return dwErrCode; } static DWORD LoadLocalIndexFiles(TCascStorage * hs) { ULONGLONG TotalSize = 0; DWORD dwIndexCount = 0; DWORD dwErrCode; // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Loading index files", NULL, 0, 0)) return ERROR_CANCELLED; // Perform the directory scan if((dwErrCode = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, hs)) == ERROR_SUCCESS) { // If no index file was found, we cannot load anything if(hs->szIndexFormat == NULL) return ERROR_FILE_NOT_FOUND; // Load each index file for(DWORD i = 0; i < CASC_INDEX_COUNT; i++) { CASC_INDEX & IndexFile = hs->IndexFiles[i]; DWORD cbFileData = 0; // Create the file name if((IndexFile.szFileName = CreateIndexFileName(hs, i, IndexFile.NewSubIndex)) == NULL) return ERROR_NOT_ENOUGH_MEMORY; // WoW6 actually reads THE ENTIRE file to memory. Verified on Mac build (x64). if((IndexFile.pbFileData = LoadFileToMemory(IndexFile.szFileName, &cbFileData)) == NULL) { // Storages downloaded by Blizzget tool don't have all index files present if((dwErrCode = GetCascError()) == ERROR_FILE_NOT_FOUND) { dwErrCode = ERROR_SUCCESS; break; } return dwErrCode; } // Add to the total size of the index files IndexFile.cbFileData = cbFileData; TotalSize += cbFileData; dwIndexCount++; } // Build the map of EKey -> IndexEKeyEntry dwErrCode = hs->IndexEKeyMap.Create((size_t)(TotalSize / sizeof(FILE_EKEY_ENTRY)), CASC_EKEY_SIZE, 0); if(dwErrCode == ERROR_SUCCESS) { dwErrCode = ProcessLocalIndexFiles(hs, InsertEncodingEKeyToMap, dwIndexCount); } } return dwErrCode; } //----------------------------------------------------------------------------- // Online index files // https://wowdev.wiki/TACT#CDN_File_Organization static DWORD CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, DWORD cbIndexFile) { FILE_INDEX_FOOTER<0x08> * pFooter08; BYTE checksum_data[0x40] = { 0 }; BYTE md5_hash[MD5_HASH_SIZE]; DWORD checksum_data_length; // Clear the entire structure memset(&InFooter, 0, sizeof(CASC_ARCINDEX_FOOTER)); // Check the variant for checksum == 0x08 pFooter08 = (FILE_INDEX_FOOTER<0x08> *)(pbIndexFile + cbIndexFile - sizeof(FILE_INDEX_FOOTER<0x08>)); if (pFooter08->Version == 1 && pFooter08->Reserved[0] == 0 && pFooter08->Reserved[1] == 0 && pFooter08->FooterHashBytes == 8) { // Copy the entire structure memcpy(InFooter.TocHash, pFooter08->TocHash, MD5_HASH_SIZE); memcpy(InFooter.FooterHash, pFooter08->FooterHash, pFooter08->FooterHashBytes); InFooter.Version = pFooter08->Version; InFooter.OffsetBytes = pFooter08->OffsetBytes; InFooter.SizeBytes = pFooter08->SizeBytes; InFooter.EKeyLength = pFooter08->EKeyLength; InFooter.FooterHashBytes = pFooter08->FooterHashBytes; InFooter.PageLength = pFooter08->PageSizeKB << 10; InFooter.ItemLength = pFooter08->EKeyLength + pFooter08->OffsetBytes + pFooter08->SizeBytes; InFooter.FooterLength = sizeof(FILE_INDEX_FOOTER<0x08>); InFooter.ElementCount = ConvertBytesToInteger_4_LE(pFooter08->ElementCount); // Verify the hash. FooterHash needs to be cleared in order to calculate footer hash properly checksum_data_length = FIELD_OFFSET(FILE_INDEX_FOOTER<0x08>, FooterHash) - FIELD_OFFSET(FILE_INDEX_FOOTER<0x08>, Version); memcpy(checksum_data, &pFooter08->Version, checksum_data_length); CascCalculateDataBlockHash(checksum_data, sizeof(FILE_INDEX_FOOTER<0x08>) - MD5_HASH_SIZE, md5_hash); if(!memcmp(md5_hash, InFooter.FooterHash, InFooter.FooterHashBytes)) return ERROR_SUCCESS; } assert(false); return ERROR_BAD_FORMAT; } static DWORD CaptureIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_EKEY_ENTRY & EKeyEntry, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd, size_t nArchive) { ULONGLONG StorageOffset = nArchive; ULONGLONG ArchiveOffset; // If there enough bytes for one entry/ if ((pbIndexPage + InFooter.ItemLength) > pbIndexPageEnd) return ERROR_BAD_FORMAT; // Capture the EKey (variable length) pbIndexPage = CaptureEncodedKey(EKeyEntry.EKey, pbIndexPage, InFooter.EKeyLength); // Copy the archive offset ArchiveOffset = ConvertBytesToInteger_X(pbIndexPage + InFooter.SizeBytes, InFooter.OffsetBytes); if (ArchiveOffset >= 0x10000000) return ERROR_BAD_FORMAT; // Capture the storage offset and encoded size EKeyEntry.StorageOffset = (StorageOffset << (InFooter.OffsetBytes * 8)) | ArchiveOffset; EKeyEntry.EncodedSize = ConvertBytesToInteger_X(pbIndexPage, InFooter.SizeBytes); EKeyEntry.Alignment = 0; // Is there a valid hash? return CascIsValidMD5(EKeyEntry.EKey) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } static DWORD VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile, LPBYTE * PtrIndexEnd) { size_t nPageCount; // Set the new length (without the footer) cbIndexFile = cbIndexFile - InFooter.FooterLength; nPageCount = cbIndexFile / (InFooter.PageLength + MD5_HASH_SIZE); // There must be equal or more pages if(((InFooter.PageLength + MD5_HASH_SIZE) * nPageCount) > cbIndexFile) return ERROR_BAD_FORMAT; // Return the end-of-index nPageCount = cbIndexFile / (InFooter.PageLength + MD5_HASH_SIZE); PtrIndexEnd[0] = pbIndexFile + (nPageCount * InFooter.PageLength); return ERROR_SUCCESS; } static DWORD LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd, size_t nArchive) { CASC_EKEY_ENTRY EKeyEntry; DWORD dwErrCode; while (pbIndexPage <= pbIndexPageEnd) { // Capture the index entry dwErrCode = CaptureIndexEntry(InFooter, EKeyEntry, pbIndexPage, pbIndexPageEnd, nArchive); if (dwErrCode != ERROR_SUCCESS) break; // Insert a new entry to the index array if((hs->IndexArray.Insert(&EKeyEntry, 1)) == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Move to the next entry pbIndexPage += InFooter.ItemLength; } return ERROR_SUCCESS; } static DWORD LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexFile, DWORD cbIndexFile, size_t nArchive) { CASC_ARCINDEX_FOOTER InFooter; LPBYTE pbIndexEnd = NULL; DWORD dwErrCode; // Validate and capture the footer dwErrCode = CaptureArcIndexFooter(InFooter, pbIndexFile, cbIndexFile); if (dwErrCode != ERROR_SUCCESS) return dwErrCode; // Remember the file offset and EKey length SaveFileOffsetBitsAndEKeyLength(hs, InFooter.OffsetBytes * 8, InFooter.EKeyLength); // Verify the size of the index file dwErrCode = VerifyIndexSize(InFooter, pbIndexFile, cbIndexFile, &pbIndexEnd); if (dwErrCode != ERROR_SUCCESS) return dwErrCode; // Parse all pages while (pbIndexFile < pbIndexEnd) { // Load the entire page dwErrCode = LoadArchiveIndexPage(hs, InFooter, pbIndexFile, pbIndexFile + InFooter.PageLength, nArchive); if (dwErrCode != ERROR_SUCCESS) break; // Move to the next page pbIndexFile += InFooter.PageLength; } return ERROR_SUCCESS; } static DWORD BuildMapOfArchiveIndices(TCascStorage * hs) { PCASC_EKEY_ENTRY pEKeyEntry; size_t nItemCount = hs->IndexArray.ItemCount(); DWORD dwErrCode; // Create the map dwErrCode = hs->IndexMap.Create(nItemCount, MD5_HASH_SIZE, FIELD_OFFSET(CASC_EKEY_ENTRY, EKey)); if (dwErrCode != ERROR_SUCCESS) return dwErrCode; // Insert all items for(size_t i = 0; i < nItemCount; i++) { pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexArray.ItemAt(i); if (pEKeyEntry != NULL) { if (!hs->IndexMap.InsertObject(pEKeyEntry, pEKeyEntry->EKey)) { return ERROR_NOT_ENOUGH_MEMORY; } } } return dwErrCode; } static DWORD LoadArchiveIndexFiles(TCascStorage * hs) { LPBYTE pbFileData; TCHAR szLocalPath[MAX_PATH]; DWORD cbFileData = 0; size_t nArchiveCount = (hs->ArchivesKey.cbData / MD5_HASH_SIZE); DWORD dwErrCode = ERROR_SUCCESS; // Create the array object for the indices dwErrCode = hs->IndexArray.Create(sizeof(CASC_EKEY_ENTRY), 0x10000); if (dwErrCode != ERROR_SUCCESS) return dwErrCode; // Load all the indices for (size_t i = 0; i < nArchiveCount; i++) { CASC_CDN_DOWNLOAD CdnsInfo = {0}; LPBYTE pbIndexHash = hs->ArchivesKey.pbData + (i * MD5_HASH_SIZE); // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Downloading archive indexes", NULL, (DWORD)(i), (DWORD)(nArchiveCount))) { dwErrCode = ERROR_CANCELLED; break; } // Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file CdnsInfo.szCdnsPath = hs->szCdnPath; CdnsInfo.szPathType = _T("data"); CdnsInfo.pbEKey = pbIndexHash; CdnsInfo.szExtension = _T(".index"); CdnsInfo.szLocalPath = szLocalPath; CdnsInfo.ccLocalPath = _countof(szLocalPath); dwErrCode = DownloadFileFromCDN(hs, CdnsInfo); // Load and parse the archive index if (dwErrCode == ERROR_SUCCESS) { // Load the index file to memory pbFileData = LoadFileToMemory(szLocalPath, &cbFileData); if (pbFileData && cbFileData) { dwErrCode = LoadArchiveIndexFile(hs, pbFileData, cbFileData, i); CASC_FREE(pbFileData); } } // Break if an error if (dwErrCode != ERROR_SUCCESS) break; } // Build map of EKey -> CASC_EKEY_ENTRY if (dwErrCode == ERROR_SUCCESS) dwErrCode = BuildMapOfArchiveIndices(hs); return dwErrCode; } //----------------------------------------------------------------------------- // Public functions bool CopyEKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry) { LPBYTE pbEKeyEntry = (LPBYTE)hs->IndexEKeyMap.FindObject(pCKeyEntry->EKey); // Don't do this on online storages if(!(hs->dwFeatures & CASC_FEATURE_ONLINE)) { // If the file was found, then copy the content to the CKey entry pbEKeyEntry = (LPBYTE)hs->IndexEKeyMap.FindObject(pCKeyEntry->EKey); if(pbEKeyEntry == NULL) return false; pCKeyEntry->StorageOffset = ConvertBytesToInteger_5(pbEKeyEntry + hs->EKeyLength); pCKeyEntry->EncodedSize = ConvertBytesToInteger_4_LE(pbEKeyEntry + hs->EKeyLength + 5); pCKeyEntry->Flags |= CASC_CE_FILE_IS_LOCAL; } return true; } DWORD LoadIndexFiles(TCascStorage * hs) { // For local storages, load the index files from the disk // For online storages, load the index files from the cache / internet if(hs->dwFeatures & CASC_FEATURE_ONLINE) { return LoadArchiveIndexFiles(hs); } else { return LoadLocalIndexFiles(hs); } } void FreeIndexFiles(TCascStorage * hs) { // Free the map of EKey -> Index Ekey item hs->IndexEKeyMap.Free(); // Free all loaded index files for(size_t i = 0; i < CASC_INDEX_COUNT; i++) { CASC_INDEX & IndexFile = hs->IndexFiles[i]; // Free the file data CASC_FREE(IndexFile.pbFileData); IndexFile.cbFileData = 0; // Free the file name CASC_FREE(IndexFile.szFileName); } } ================================================ FILE: deps/CascLib/src/CascLib.h ================================================ /*****************************************************************************/ /* CascLib.h Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* CascLib library v 1.00 */ /* */ /* Author : Ladislav Zezula */ /* E-mail : ladik@zezula.net */ /* WWW : http://www.zezula.net */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad Created */ /*****************************************************************************/ #ifndef __CASCLIB_H__ #define __CASCLIB_H__ #ifdef _MSC_VER #pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' #pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy' #endif #include "CascPort.h" #ifdef __cplusplus extern "C" { #endif //----------------------------------------------------------------------------- // Use the apropriate library // // The library type is encoded in the library name as the following // CascLibXYZ.lib // // X - D for Debug version, R for Release version // Y - A for ANSI version, U for Unicode version // Z - S for static-linked CRT library, D for dynamic CRT library (dll) // #if defined(_MSC_VER) && !defined(__CASCLIB_SELF__) && !defined(CASCLIB_NO_AUTO_LINK_LIBRARY) #ifndef WDK_BUILD #ifdef _DEBUG // DEBUG VERSIONS #ifndef _UNICODE #ifdef _DLL #pragma comment(lib, "CascLibDAD.lib") // Debug Ansi CRT-DLL version #else #pragma comment(lib, "CascLibDAS.lib") // Debug Ansi CRT-LIB version #endif #else #ifdef _DLL #pragma comment(lib, "CascLibDUD.lib") // Debug Unicode CRT-DLL version #else #pragma comment(lib, "CascLibDUS.lib") // Debug Unicode CRT-LIB version #endif #endif #else // RELEASE VERSIONS #ifndef _UNICODE #ifdef _DLL #pragma comment(lib, "CascLibRAD.lib") // Release Ansi CRT-DLL version #else #pragma comment(lib, "CascLibRAS.lib") // Release Ansi CRT-LIB version #endif #else #ifdef _DLL #pragma comment(lib, "CascLibRUD.lib") // Release Unicode CRT-DLL version #else #pragma comment(lib, "CascLibRUS.lib") // Release Unicode CRT-LIB version #endif #endif #endif #endif #endif //----------------------------------------------------------------------------- // Defines #define CASCLIB_VERSION 0x0210 // CascLib version - integral (2.1) #define CASCLIB_VERSION_STRING "2.1" // CascLib version - string // Values for CascOpenFile #define CASC_OPEN_BY_NAME 0x00000000 // Open the file by name. This is the default value #define CASC_OPEN_BY_CKEY 0x00000001 // The name is just the content key; skip ROOT file processing #define CASC_OPEN_BY_EKEY 0x00000002 // The name is just the encoded key; skip ROOT file processing #define CASC_OPEN_BY_FILEID 0x00000003 // The name is CASC_FILE_DATA_ID(FileDataId) #define CASC_OPEN_TYPE_MASK 0x0000000F // The mask which gets open type from the dwFlags #define CASC_OPEN_FLAGS_MASK 0xFFFFFFF0 // The mask which gets open type from the dwFlags #define CASC_STRICT_DATA_CHECK 0x00000010 // Verify all data read from a file #define CASC_OVERCOME_ENCRYPTED 0x00000020 // When CascReadFile encounters a block encrypted with a key that is missing, the block is filled with zeros and returned as success #define CASC_LOCALE_ALL 0xFFFFFFFF #define CASC_LOCALE_ALL_WOW 0x0001F3F6 // All except enCN and enTW #define CASC_LOCALE_NONE 0x00000000 #define CASC_LOCALE_UNKNOWN1 0x00000001 #define CASC_LOCALE_ENUS 0x00000002 #define CASC_LOCALE_KOKR 0x00000004 #define CASC_LOCALE_RESERVED 0x00000008 #define CASC_LOCALE_FRFR 0x00000010 #define CASC_LOCALE_DEDE 0x00000020 #define CASC_LOCALE_ZHCN 0x00000040 #define CASC_LOCALE_ESES 0x00000080 #define CASC_LOCALE_ZHTW 0x00000100 #define CASC_LOCALE_ENGB 0x00000200 #define CASC_LOCALE_ENCN 0x00000400 #define CASC_LOCALE_ENTW 0x00000800 #define CASC_LOCALE_ESMX 0x00001000 #define CASC_LOCALE_RURU 0x00002000 #define CASC_LOCALE_PTBR 0x00004000 #define CASC_LOCALE_ITIT 0x00008000 #define CASC_LOCALE_PTPT 0x00010000 // Content flags on WoW #define CASC_CFLAG_LOAD_ON_WINDOWS 0x08 #define CASC_CFLAG_LOAD_ON_MAC 0x10 #define CASC_CFLAG_LOW_VIOLENCE 0x80 #define CASC_CFLAG_DONT_LOAD 0x100 #define CASC_CFLAG_NO_NAME_HASH 0x10000000 #define CASC_CFLAG_BUNDLE 0x40000000 #define CASC_CFLAG_NO_COMPRESSION 0x80000000 #ifndef MD5_HASH_SIZE #define MD5_HASH_SIZE 0x10 #define MD5_STRING_SIZE 0x20 #endif // Return value for CascGetFileSize and CascSetFilePointer #define CASC_INVALID_INDEX 0xFFFFFFFF #define CASC_INVALID_SIZE 0xFFFFFFFF #define CASC_INVALID_POS 0xFFFFFFFF #define CASC_INVALID_ID 0xFFFFFFFF #define CASC_INVALID_OFFS64 0xFFFFFFFFFFFFFFFF #define CASC_INVALID_SIZE64 0xFFFFFFFFFFFFFFFF // Flags for CASC_STORAGE_FEATURES::dwFeatures #define CASC_FEATURE_FILE_NAMES 0x00000001 // File names are supported by the storage #define CASC_FEATURE_ROOT_CKEY 0x00000002 // Present if the storage's ROOT returns CKey #define CASC_FEATURE_TAGS 0x00000004 // Tags are supported by the storage #define CASC_FEATURE_FNAME_HASHES 0x00000008 // The storage contains file name hashes on ALL files #define CASC_FEATURE_FNAME_HASHES_OPTIONAL 0x00000010 // The storage contains file name hashes for SOME files #define CASC_FEATURE_FILE_DATA_IDS 0x00000020 // The storage indexes files by FileDataId #define CASC_FEATURE_LOCALE_FLAGS 0x00000040 // Locale flags are supported #define CASC_FEATURE_CONTENT_FLAGS 0x00000080 // Content flags are supported #define CASC_FEATURE_ONLINE 0x00000100 // The storage is an online storage // Macro to convert FileDataId to the argument of CascOpenFile #define CASC_FILE_DATA_ID(FileDataId) ((LPCSTR)(size_t)FileDataId) #define CASC_FILE_DATA_ID_FROM_STRING(szFileName) ((DWORD)(size_t)szFileName) // Maximum length of encryption key #define CASC_KEY_LENGTH 0x10 //----------------------------------------------------------------------------- // Structures typedef enum _CASC_STORAGE_INFO_CLASS { // Returns the number of local files in the storage. Note that files // can exist under different names, so the total number of files in the archive // can be higher than the value returned by this info class CascStorageLocalFileCount, // Returns the total file count, including the offline files CascStorageTotalFileCount, CascStorageFeatures, // Returns the features flag CascStorageInstalledLocales, // Not supported CascStorageProduct, // Gives CASC_STORAGE_PRODUCT CascStorageTags, // Gives CASC_STORAGE_TAGS structure CascStoragePathProduct, // Gives Path:Product into a LPTSTR buffer CascStorageInfoClassMax } CASC_STORAGE_INFO_CLASS, *PCASC_STORAGE_INFO_CLASS; typedef enum _CASC_FILE_INFO_CLASS { CascFileContentKey, CascFileEncodedKey, CascFileFullInfo, // Gives CASC_FILE_FULL_INFO structure CascFileSpanInfo, // Gives CASC_FILE_SPAN_INFO structure for each file span CascFileInfoClassMax } CASC_FILE_INFO_CLASS, *PCASC_FILE_INFO_CLASS; // CascLib may provide a fake name, constructed from file data id, CKey or EKey. // This enum helps to see what name was actually returned // Note that any of these names can be passed to CascOpenFile with no extra flags typedef enum _CASC_NAME_TYPE { CascNameFull, // Fully qualified file name CascNameDataId, // Name created from file data id (FILE%08X.dat) CascNameCKey, // Name created as string representation of CKey CascNameEKey // Name created as string representation of EKey } CASC_NAME_TYPE, *PCASC_NAME_TYPE; // Structure for SFileFindFirstFile and SFileFindNextFile typedef struct _CASC_FIND_DATA { // Full name of the found file. In case when this is CKey/EKey, // this will be just string representation of the key stored in 'FileKey' char szFileName[MAX_PATH]; // Content key. This is present if the CASC_FEATURE_ROOT_CKEY is present BYTE CKey[MD5_HASH_SIZE]; // Encoded key. This is always present. BYTE EKey[MD5_HASH_SIZE]; // Tag mask. Only valid if the storage supports tags, otherwise 0 ULONGLONG TagBitMask; // Size of the file, as retrieved from CKey entry ULONGLONG FileSize; // Plain name of the found file. Pointing inside the 'szFileName' array char * szPlainName; // File data ID. Only valid if the storage supports file data IDs, otherwise CASC_INVALID_ID DWORD dwFileDataId; // Locale flags. Only valid if the storage supports locale flags, otherwise CASC_INVALID_ID DWORD dwLocaleFlags; // Content flags. Only valid if the storage supports content flags, otherwise CASC_INVALID_ID DWORD dwContentFlags; // Span count DWORD dwSpanCount; // If true the file is available locally DWORD bFileAvailable:1; // Name type in 'szFileName'. In case the file name is not known, // CascLib can put FileDataId-like name or a string representation of CKey/EKey CASC_NAME_TYPE NameType; } CASC_FIND_DATA, *PCASC_FIND_DATA; typedef struct _CASC_STORAGE_TAG { LPCSTR szTagName; // Tag name (zero terminated, ANSI) DWORD TagNameLength; // Length of the tag name DWORD TagValue; // Tag value } CASC_STORAGE_TAG, *PCASC_STORAGE_TAG; typedef struct _CASC_STORAGE_TAGS { size_t TagCount; // Number of items in the Tags array size_t Reserved; // Reserved for future use CASC_STORAGE_TAG Tags[1]; // Array of CASC tags } CASC_STORAGE_TAGS, *PCASC_STORAGE_TAGS; typedef struct _CASC_STORAGE_PRODUCT { char szCodeName[0x1C]; // Code name of the product ("wowt" = "World of Warcraft PTR") DWORD BuildNumber; // Build number. If zero, then CascLib didn't recognize build number } CASC_STORAGE_PRODUCT, *PCASC_STORAGE_PRODUCT; typedef struct _CASC_FILE_FULL_INFO { BYTE CKey[MD5_HASH_SIZE]; // CKey BYTE EKey[MD5_HASH_SIZE]; // EKey char DataFileName[0x10]; // Plain name of the data file where the file is stored ULONGLONG StorageOffset; // Offset of the file over the entire storage ULONGLONG SegmentOffset; // Offset of the file in the segment file ("data.###") ULONGLONG TagBitMask; // Bitmask of tags. Zero if not supported ULONGLONG FileNameHash; // Hash of the file name. Zero if not supported ULONGLONG ContentSize; // Content size of all spans ULONGLONG EncodedSize; // Encoded size of all spans DWORD SegmentIndex; // Index of the segment file (aka 0 = "data.000") DWORD SpanCount; // Number of spans forming the file DWORD FileDataId; // File data ID. CASC_INVALID_ID if not supported. DWORD LocaleFlags; // Locale flags. CASC_INVALID_ID if not supported. DWORD ContentFlags; // Locale flags. CASC_INVALID_ID if not supported } CASC_FILE_FULL_INFO, *PCASC_FILE_FULL_INFO; typedef struct _CASC_FILE_SPAN_INFO { BYTE CKey[MD5_HASH_SIZE]; // Content key of the file span BYTE EKey[MD5_HASH_SIZE]; // Encoded key of the file span ULONGLONG StartOffset; // Starting offset of the file span ULONGLONG EndOffset; // Ending offset of the file span DWORD ArchiveIndex; // Index of the archive DWORD ArchiveOffs; // Offset in the archive DWORD HeaderSize; // Size of encoded frame headers DWORD FrameCount; // Number of frames in this span } CASC_FILE_SPAN_INFO, *PCASC_FILE_SPAN_INFO; //----------------------------------------------------------------------------- // Extended version of CascOpenStorage // Some operations (e.g. opening an online storage) may take long time. // This callback allows an application to be notified about loading progress // and even cancel the storage loading process typedef bool (WINAPI * PFNPROGRESSCALLBACK)( // Return 'true' to cancel the loading process void * PtrUserParam, // User-specific parameter passed to the callback LPCSTR szWork, // Text for the current activity (example: "Loading "ENCODING" file") LPCSTR szObject, // (optional) name of the object tied to the activity (example: index file name) DWORD CurrentValue, // (optional) current object being processed DWORD TotalValue // (optional) If non-zero, this is the total number of objects to process ); // Some storages support multi-product installation (e.g. World of Warcraft). // With this callback, the calling application can specify which storage to open typedef bool (WINAPI * PFNPRODUCTCALLBACK)( // Return 'true' to cancel the loading process void * PtrUserParam, // User-specific parameter passed to the callback LPCSTR * ProductList, // Array of product codenames found in the storage size_t ProductCount, // Number of products in the ProductList array size_t * PtrSelectedProduct // [out] This is the selected product to open. On input, set to 0 (aka the first product) ); typedef struct _CASC_OPEN_STORAGE_ARGS { size_t Size; // Length of this structure. Initialize to sizeof(CASC_OPEN_STORAGE_ARGS) LPCTSTR szLocalPath; // Local: Path to the storage directory (where ".build.info: is) or any of the sub-path // Online: Path to the local storage cache LPCTSTR szCodeName; // If non-null, this will specify a product in a multi-product local storage // Has higher priority than PfnProductCallback (if both specified) LPCTSTR szRegion; // If non-null, this will specify a product region. PFNPROGRESSCALLBACK PfnProgressCallback; // Progress callback. If non-NULL, this can inform the caller about state of the opening storage void * PtrProgressParam; // Pointer-sized parameter that will be passed to PfnProgressCallback PFNPRODUCTCALLBACK PfnProductCallback; // Progress callback. If non-NULL, will be called on multi-product storage to select one of the products void * PtrProductParam; // Pointer-sized parameter that will be passed to PfnProgressCallback DWORD dwLocaleMask; // Locale mask to open DWORD dwFlags; // Reserved. Set to zero. // // Any additional member from here on must be checked for availability using the ExtractVersionedArgument function. // Example: // // LPCTSTR szBuildKey = NULL; // ExtractVersionedArgument(pArgs, offsetof(CASC_OPEN_STORAGE_ARGS, szBuildId), &szBuildKey); // LPCTSTR szBuildKey; // If non-null, this will specify a build key (aka MD5 of build config that is different that current online version) } CASC_OPEN_STORAGE_ARGS, *PCASC_OPEN_STORAGE_ARGS; //----------------------------------------------------------------------------- // Functions for storage manipulation bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, bool bOnlineStorage, HANDLE * phStorage); bool WINAPI CascOpenStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage); bool WINAPI CascOpenOnlineStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage); bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded); bool WINAPI CascCloseStorage(HANDLE hStorage); bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * PtrFileHandle); bool WINAPI CascOpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle); bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded); bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize); bool WINAPI CascSetFilePointer64(HANDLE hFile, LONGLONG DistanceToMove, PULONGLONG PtrNewPos, DWORD dwMoveMethod); bool WINAPI CascReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, PDWORD pdwRead); bool WINAPI CascCloseFile(HANDLE hFile); DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh); DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * PtrFilePosHigh, DWORD dwMoveMethod); HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, LPCSTR szMask, PCASC_FIND_DATA pFindData, LPCTSTR szListFile); bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData); bool WINAPI CascFindClose(HANDLE hFind); bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key); bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey); bool WINAPI CascImportKeysFromString(HANDLE hStorage, LPCSTR szKeyList); bool WINAPI CascImportKeysFromFile(HANDLE hStorage, LPCTSTR szFileName); LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName); bool WINAPI CascGetNotFoundEncryptionKey(HANDLE hStorage, ULONGLONG * KeyName); //----------------------------------------------------------------------------- // Error code support void SetCascError(DWORD dwErrCode); DWORD GetCascError(); #ifdef __cplusplus } // extern "C" #endif #endif // __CASCLIB_H__ ================================================ FILE: deps/CascLib/src/CascOpenFile.cpp ================================================ /*****************************************************************************/ /* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* System-dependent directory functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 01.05.14 1.00 Lad The first version of CascOpenFile.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // TCascFile class functions TCascFile::TCascFile(TCascStorage * ahs, PCASC_CKEY_ENTRY apCKeyEntry) { // Reference the storage handle if((hs = ahs) != NULL) hs->AddRef(); ClassName = CASC_MAGIC_FILE; FilePointer = 0; pCKeyEntry = apCKeyEntry; SpanCount = (pCKeyEntry->SpanCount != 0) ? pCKeyEntry->SpanCount : 1; bVerifyIntegrity = false; bDownloadFileIf = false; bCloseFileStream = false; bFreeCKeyEntries = false; // Allocate the array of file spans if((pFileSpan = CASC_ALLOC_ZERO(SpanCount)) != NULL) { InitFileSpans(pFileSpan, SpanCount); InitCacheStrategy(); } } TCascFile::~TCascFile() { // Free all stuff related to file spans if (pFileSpan != NULL) { PCASC_FILE_SPAN pSpanPtr = pFileSpan; for(DWORD i = 0; i < SpanCount; i++, pSpanPtr++) { // Close the span file stream if this is a local file if(bCloseFileStream) FileStream_Close(pSpanPtr->pStream); pSpanPtr->pStream = NULL; // Free the span frames CASC_FREE(pSpanPtr->pFrames); } CASC_FREE(pFileSpan); } // Free the CKey entries, if needed if(pCKeyEntry && bFreeCKeyEntries) delete [] pCKeyEntry; pCKeyEntry = NULL; // Free the file cache CASC_FREE(pbFileCache); // Close (dereference) the archive handle if(hs != NULL) hs = hs->Release(); ClassName = 0; } DWORD TCascFile::OpenFileSpans(LPCTSTR szSpanList) { TFileStream * pStream; ULONGLONG FileSize = 0; DWORD dwErrCode = ERROR_SUCCESS; for(DWORD i = 0; i < SpanCount; i++) { // Open the file span pFileSpan[i].pStream = pStream = FileStream_OpenFile(szSpanList, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT); if(pFileSpan[i].pStream == NULL) { dwErrCode = GetCascError(); break; } // If succeeded, we assign the span to the FileStream_GetSize(pStream, &FileSize); if((FileSize >> 0x1E) != 0) { dwErrCode = ERROR_NOT_SUPPORTED; break; } pCKeyEntry[i].EncodedSize = (DWORD)FileSize; } // Free the so-far-opened files if(dwErrCode != ERROR_SUCCESS) { for(DWORD i = 0; i < SpanCount; i++) { if(pFileSpan[i].pStream != NULL) FileStream_Close(pFileSpan[i].pStream); pFileSpan[i].pStream = NULL; } } return dwErrCode; } void TCascFile::InitFileSpans(PCASC_FILE_SPAN pSpans, DWORD dwSpanCount) { ULONGLONG FileOffsetBits = 30; ULONGLONG FileOffsetMask = 0; ULONGLONG FileOffset = 0; // Initialize the file sizes. Note that if any of the spans has invalid size, // the entire file size will be set to CASC_INVALID_SIZE64. GetFileSpanInfo(pCKeyEntry, &ContentSize, &EncodedSize); // Resolve the file offset bits and file offset mask if(hs != NULL) FileOffsetBits = hs->FileOffsetBits; FileOffsetMask = ((ULONGLONG)1 << FileOffsetBits) - 1; // Add all span sizes for(DWORD i = 0; i < dwSpanCount; i++, pSpans++) { // Put the archive index and archive offset pSpans->ArchiveIndex = (DWORD)(pCKeyEntry[i].StorageOffset >> FileOffsetBits); pSpans->ArchiveOffs = (DWORD)(pCKeyEntry[i].StorageOffset & FileOffsetMask); // Add to the total encoded size if(ContentSize != CASC_INVALID_SIZE64) { pSpans->StartOffset = FileOffset; FileOffset = FileOffset + pCKeyEntry[i].ContentSize; pSpans->EndOffset = FileOffset; } } } void TCascFile::InitCacheStrategy() { CacheStrategy = CascCacheLastFrame; FileCacheStart = FileCacheEnd = 0; pbFileCache = NULL; } //----------------------------------------------------------------------------- // Local functions static size_t GetSpanFileCount(LPTSTR szSpanList) { LPTSTR szSpanPtr = szSpanList; size_t nSpanCount = 1; while(szSpanPtr[0] != 0) { // End of a file? if(szSpanPtr[0] == ';' && szSpanPtr[1] != 0) { szSpanPtr[0] = 0; nSpanCount++; } szSpanPtr++; } // Place an additional zero to make the list terminated by double EOS szSpanPtr[1] = 0; return nSpanCount; } PCASC_CKEY_ENTRY FindCKeyEntry_CKey(TCascStorage * hs, LPBYTE pbCKey, PDWORD PtrIndex) { return (PCASC_CKEY_ENTRY)hs->CKeyMap.FindObject(pbCKey, PtrIndex); } PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD PtrIndex) { return (PCASC_CKEY_ENTRY)hs->EKeyMap.FindObject(pbEKey, PtrIndex); } bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle) { TCascFile * hf = NULL; DWORD dwErrCode = ERROR_FILE_NOT_FOUND; // If the CKey entry is NULL, we consider the file non-existant if(pCKeyEntry != NULL) { // Create the file handle structure if((hf = new TCascFile(hs, pCKeyEntry)) != NULL) { hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false; hf->bDownloadFileIf = (hs->dwFeatures & CASC_FEATURE_ONLINE) ? true : false; hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false; dwErrCode = ERROR_SUCCESS; } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } // Give the output parameter, no matter what PtrFileHandle[0] = (HANDLE)hf; // Handle last error if(dwErrCode != ERROR_SUCCESS) SetCascError(dwErrCode); return (dwErrCode == ERROR_SUCCESS); } bool OpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle) { PCASC_CKEY_ENTRY pCKeyEntry; TCascFile * hf = NULL; LPTSTR szSpanList; size_t nSpanCount; DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Create a copy of the file name. It is actually a file name list, // separated by comma (for supporting multi-span files) if((szSpanList = CascNewStr(szFileName, 1)) != NULL) { // Calculate the span count if((nSpanCount = GetSpanFileCount(szSpanList)) != 0 || nSpanCount > 0xFF) { // Allocate CKey array for the file. Each entry describes one file span if((pCKeyEntry = new CASC_CKEY_ENTRY[nSpanCount]) != NULL) { // Prepare the span count to the first item pCKeyEntry->SpanCount = (BYTE)nSpanCount; // Prepare the archive offset in each CKey entry for(size_t i = 0; i < nSpanCount; i++) pCKeyEntry[i].StorageOffset = 0; // Create an instance of the TCascFile if((hf = new TCascFile(NULL, pCKeyEntry)) != NULL) { // Prepare the structure hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false; hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false; hf->bCloseFileStream = true; // Open all local file spans dwErrCode = hf->OpenFileSpans(szSpanList); if(dwErrCode != ERROR_SUCCESS) { delete hf; hf = NULL; } } } } else { dwErrCode = ERROR_INVALID_PARAMETER; } delete [] szSpanList; } // Give the output parameter, no matter what PtrFileHandle[0] = (HANDLE)hf; // Handle last error if(dwErrCode != ERROR_SUCCESS) SetCascError(dwErrCode); return (dwErrCode == ERROR_SUCCESS); } bool SetCacheStrategy(HANDLE hFile, CSTRTG CacheStrategy) { TCascFile * hf; // Validate the file handle if((hf = TCascFile::IsValid(hFile)) != NULL) { // The cache must not be initialized yet if(hf->pbFileCache == NULL) { hf->CacheStrategy = CacheStrategy; return true; } } // Failed. This should never happen assert(false); return false; } //----------------------------------------------------------------------------- // Public functions bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * PtrFileHandle) { PCASC_CKEY_ENTRY pCKeyEntry = NULL; TCascStorage * hs; const char * szFileName; DWORD FileDataId = CASC_INVALID_ID; BYTE CKeyEKeyBuffer[MD5_HASH_SIZE]; DWORD dwErrCode = ERROR_SUCCESS; // This parameter is not used CASCLIB_UNUSED(dwLocaleFlags); // Validate the storage handle hs = TCascStorage::IsValid(hStorage); if(hs == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // Validate the other parameters if(PtrFileHandle == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Retrieve the CKey/EKey from the file name in different modes switch(dwOpenFlags & CASC_OPEN_TYPE_MASK) { case CASC_OPEN_BY_NAME: // The 'pvFileName' must be zero terminated ANSI file name szFileName = (const char *)pvFileName; if(szFileName == NULL || szFileName[0] == 0) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // The first chance: Try to find the file by name (using the root handler) pCKeyEntry = hs->pRootHandler->GetFile(hs, szFileName); if(pCKeyEntry != NULL) break; // Second chance: If the file name is actually a file data id, we convert it to file data ID if(IsFileDataIdName(szFileName, FileDataId)) { pCKeyEntry = hs->pRootHandler->GetFile(hs, FileDataId); if(pCKeyEntry != NULL) break; } // Third chance: If the file name is a string representation of CKey/EKey, we try to query for CKey if(IsFileCKeyEKeyName(szFileName, CKeyEKeyBuffer)) { pCKeyEntry = FindCKeyEntry_CKey(hs, CKeyEKeyBuffer); if(pCKeyEntry != NULL) break; pCKeyEntry = FindCKeyEntry_EKey(hs, CKeyEKeyBuffer); if(pCKeyEntry != NULL) break; } SetCascError(ERROR_FILE_NOT_FOUND); return false; case CASC_OPEN_BY_CKEY: // The 'pvFileName' must be a pointer to 16-byte CKey or EKey if(pvFileName == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Search the CKey map in order to find the CKey entry pCKeyEntry = FindCKeyEntry_CKey(hs, (LPBYTE)pvFileName); break; case CASC_OPEN_BY_EKEY: // The 'pvFileName' must be a pointer to 16-byte CKey or EKey if(pvFileName == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Search the CKey map in order to find the CKey entry pCKeyEntry = FindCKeyEntry_EKey(hs, (LPBYTE)pvFileName); break; case CASC_OPEN_BY_FILEID: // Retrieve the file CKey/EKey pCKeyEntry = hs->pRootHandler->GetFile(hs, CASC_FILE_DATA_ID_FROM_STRING(pvFileName)); break; default: // Unknown open mode dwErrCode = ERROR_INVALID_PARAMETER; break; } // Perform the open operation return OpenFileByCKeyEntry(hs, pCKeyEntry, dwOpenFlags, PtrFileHandle); } bool WINAPI CascOpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle) { // Verify parameters if(szFileName == NULL || szFileName[0] == 0 || PtrFileHandle == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } return OpenLocalFile(szFileName, dwOpenFlags, PtrFileHandle); } bool WINAPI CascCloseFile(HANDLE hFile) { TCascFile * hf; hf = TCascFile::IsValid(hFile); if (hf != NULL) { delete hf; return true; } SetCascError(ERROR_INVALID_HANDLE); return false; } ================================================ FILE: deps/CascLib/src/CascOpenStorage.cpp ================================================ /*****************************************************************************/ /* CascOpenStorage.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Storage functions for CASC */ /* Note: WoW6 offsets refer to WoW.exe 6.0.3.19116 (32-bit) */ /* SHA1: c10e9ffb7d040a37a356b96042657e1a0c95c0dd */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad The first version of CascOpenStorage.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" #ifdef INTERLOCKED_NOT_SUPPORTED #pragma error Interlocked operations are not supported on this architecture. Multi-threaded access to CASC storages will not work properly. #endif //----------------------------------------------------------------------------- // Local defines // Limit for "additional" items in CKey table #define CASC_MAX_EXTRA_ITEMS 0x40 //----------------------------------------------------------------------------- // DEBUG functions #define CHECKED_KEY {0x00, 0x00, 0x0F, 0x84} #if defined(_DEBUG) && defined(CHECKED_KEY) inline bool CheckForXKey(LPBYTE XKey) { BYTE CheckedKey[] = CHECKED_KEY; for(size_t i = 0; i < _countof(CheckedKey); i++) { if(XKey[i] != CheckedKey[i]) return false; } return true; } #define BREAK_ON_WATCHED(XKey) if(CheckForXKey((LPBYTE)XKey)) { __debugbreak(); } #else #define BREAK_ON_WATCHED(XKey) { /* NOTHING */ } #endif //----------------------------------------------------------------------------- // TCascStorage class functions TCascStorage::TCascStorage() { // Prepare the base storage parameters ClassName = CASC_MAGIC_STORAGE; pRootHandler = NULL; dwRefCount = 1; szRootPath = szDataPath = szIndexPath = szBuildFile = szCdnServers = szCdnPath = szCodeName = NULL; szIndexFormat = NULL; szRegion = NULL; szBuildKey = NULL; memset(DataFiles, 0, sizeof(DataFiles)); memset(IndexFiles, 0, sizeof(IndexFiles)); CascInitLock(StorageLock); dwDefaultLocale = 0; dwBuildNumber = 0; dwFeatures = 0; BuildFileType = CascBuildNone; LastFailKeyName = 0; LocalFiles = TotalFiles = EKeyEntries = EKeyLength = FileOffsetBits = 0; pArgs = NULL; } TCascStorage::~TCascStorage() { // Free the root handler if(pRootHandler != NULL) delete pRootHandler; pRootHandler = NULL; // Close all data files for(size_t i = 0; i < CASC_MAX_DATA_FILES; i++) { FileStream_Close(DataFiles[i]); DataFiles[i] = NULL; } // Cleanup space occupied by index files FreeIndexFiles(this); // Cleanup the lock CascFreeLock(StorageLock); // Free the file paths CASC_FREE(szDataPath); CASC_FREE(szRootPath); CASC_FREE(szBuildFile); CASC_FREE(szIndexPath); CASC_FREE(szCdnServers); CASC_FREE(szCdnPath); CASC_FREE(szCodeName); CASC_FREE(szRegion); CASC_FREE(szBuildKey); // Free the blobs FreeCascBlob(&CdnConfigKey); FreeCascBlob(&CdnBuildKey); FreeCascBlob(&ArchiveGroup); FreeCascBlob(&ArchivesKey); FreeCascBlob(&PatchArchivesKey); FreeCascBlob(&PatchArchivesGroup); FreeCascBlob(&BuildFiles); ClassName = 0; } TCascStorage * TCascStorage::AddRef() { // Need this to be atomic to make multi-threaded file opens work CascInterlockedIncrement(&dwRefCount); return this; } TCascStorage * TCascStorage::Release() { // If the reference count reached zero, we close the archive // Need this to be atomic to make multi-threaded file opens work if(CascInterlockedDecrement(&dwRefCount) == 0) { // Release all references in the socket cache if(dwFeatures & CASC_FEATURE_ONLINE) sockets_set_caching(false); // Delete the object and return NULL delete this; return NULL; } return this; } //----------------------------------------------------------------------------- // Local functions void * ProbeOutputBuffer(void * pvBuffer, size_t cbLength, size_t cbMinLength, size_t * pcbLengthNeeded) { // Verify the output length if(cbLength < cbMinLength) { SetCascError(ERROR_INSUFFICIENT_BUFFER); pvBuffer = NULL; } // Give the output length and return result if(pcbLengthNeeded != NULL) pcbLengthNeeded[0] = cbMinLength; return pvBuffer; } static LPTSTR CheckForIndexDirectory(TCascStorage * hs, LPCTSTR szSubDir) { TCHAR szIndexPath[MAX_PATH]; // Combine the index path CombinePath(szIndexPath, _countof(szIndexPath), hs->szDataPath, szSubDir, NULL); // Check whether the path exists if(!DirectoryExists(szIndexPath)) return NULL; return CascNewStr(szIndexPath); } // Inserts an entry from the text build file static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_CKEY_ENTRY & CKeyEntry) { PCASC_CKEY_ENTRY pCKeyEntry = NULL; // Stop on file-of-interest BREAK_ON_WATCHED(CKeyEntry.EKey); // Skip entries without any key if(CKeyEntry.Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY)) { // Check if there is an existing entry if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKeyEntry.CKey)) == NULL) { // Insert a new entry to the array. DO NOT ALLOW enlarge array here pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false); if(pCKeyEntry == NULL) return NULL; // Fill in the item memcpy(pCKeyEntry, &CKeyEntry, sizeof(CASC_CKEY_ENTRY)); // If we have CKey present, insert it to the CKey map if(CKeyEntry.Flags & CASC_CE_HAS_CKEY) hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey); // If we have EKey present, insert it to the EKey map if(CKeyEntry.Flags & CASC_CE_HAS_EKEY) hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey); } else { if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE) pCKeyEntry->ContentSize = CKeyEntry.ContentSize; if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE) pCKeyEntry->EncodedSize = CKeyEntry.EncodedSize; } } return pCKeyEntry; } // Inserts an entry from ENCODING static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, PFILE_CKEY_ENTRY pFileEntry) { PCASC_CKEY_ENTRY pCKeyEntry; // Stop on file-of-interest BREAK_ON_WATCHED(pFileEntry->EKey); // Insert a new entry to the array. DO NOT ALLOW enlarge array here pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false); if(pCKeyEntry != NULL) { // Initialize the entry CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey); CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey); pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64; pCKeyEntry->TagBitMask = 0; pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize); pCKeyEntry->EncodedSize = CASC_INVALID_SIZE; pCKeyEntry->Flags = CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING; pCKeyEntry->RefCount = 0; pCKeyEntry->SpanCount = 1; pCKeyEntry->Priority = 0; // Copy the information from index files to the CKey entry CopyEKeyEntry(hs, pCKeyEntry); // Insert the item into both maps hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey); hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey); } else { assert(false); } return pCKeyEntry; } // Inserts an entry from DOWNLOAD static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_DOWNLOAD_ENTRY & DlEntry) { PCASC_CKEY_ENTRY pCKeyEntry; // Stop on file-of-interest BREAK_ON_WATCHED(DlEntry.EKey); // Check whether the entry is already there if((pCKeyEntry = FindCKeyEntry_EKey(hs, DlEntry.EKey)) == NULL) { // Insert dummy CKey entry to the array. DO NOT allow to enlarge the array pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false); if(pCKeyEntry == NULL) { assert(false); return NULL; } // Copy the entry ZeroMemory16(pCKeyEntry->CKey); CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey); pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64; pCKeyEntry->TagBitMask = 0; pCKeyEntry->ContentSize = CASC_INVALID_SIZE; pCKeyEntry->EncodedSize = (DWORD)DlEntry.EncodedSize; pCKeyEntry->Flags = CASC_CE_HAS_EKEY | CASC_CE_IN_DOWNLOAD; pCKeyEntry->RefCount = 0; pCKeyEntry->SpanCount = 1; // Copy the information from index files to the CKey entry CopyEKeyEntry(hs, pCKeyEntry); // Insert the entry to the map. Only insert it to the EKey map, as there is no CKey present hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey); } else { // Copy the EKey if we only have partial one if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY_PARTIAL) CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey); // Supply the encoded size, if unknown yet if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE) pCKeyEntry->EncodedSize = (DWORD)DlEntry.EncodedSize; pCKeyEntry->Flags = (pCKeyEntry->Flags & ~CASC_CE_HAS_EKEY_PARTIAL) | CASC_CE_IN_DOWNLOAD; } // Supply the rest pCKeyEntry->Priority = DlEntry.Priority; return pCKeyEntry; } static DWORD CopyBuildFileItemsToCKeyArray(TCascStorage * hs) { // Insert the well-known files // InsertCKeyEntry(hs, hs->EncodingCKey); InsertCKeyEntry(hs, hs->DownloadCKey); InsertCKeyEntry(hs, hs->InstallCKey); InsertCKeyEntry(hs, hs->PatchFile); InsertCKeyEntry(hs, hs->RootFile); InsertCKeyEntry(hs, hs->SizeFile); InsertCKeyEntry(hs, hs->VfsRoot); // Insert all VFS roots for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++) { PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i); InsertCKeyEntry(hs, *pCKeyEntry); } return ERROR_SUCCESS; } // Estimate the total number of files, so we won't have to re-allocate arrays and maps // and thus speed-up storage loading. In theory, we could guess the file count by // measuring size of ENCODING or DOWNLOAD manifests. static size_t GetEstimatedNumberOfFiles(TCascStorage * hs) { size_t nNumberOfFiles1 = 0; size_t nNumberOfFiles2 = 0; // If we know the size of DOWNLOAD at this point, we estimate number of files from it. // Size of one entry in DOWNLOAD is at least 22 bytes. This is the most reliable method. // However, for some online storages ("agent"), this is a very small value if(hs->DownloadCKey.ContentSize != CASC_INVALID_SIZE) nNumberOfFiles1 = (hs->DownloadCKey.ContentSize / sizeof(FILE_DOWNLOAD_ENTRY)) + CASC_MAX_EXTRA_ITEMS; // If we know the size of ENCODING at this point, we estimate number of files from it. // Size of one entry in ENCODING is at least 38 bytes. This method fails on storages // with TVFS file system, as ENCODING only contains a small subset of file. // Fortunately, all known TVFS-based storages have "download-size" present if(hs->EncodingCKey.ContentSize != CASC_INVALID_SIZE) nNumberOfFiles2 = (hs->EncodingCKey.ContentSize / sizeof(FILE_CKEY_ENTRY)) + CASC_MAX_EXTRA_ITEMS; // Do we know any of them? if(nNumberOfFiles1 || nNumberOfFiles2) return CASCLIB_MAX(nNumberOfFiles1, nNumberOfFiles2); // Older storages (HOTS before 39445, WoW before 19116) don't state sizes of ENCODING // and DOWNLOAD in the Build Config files. Solution: Assume there is max 1M of files return 1000000; } static DWORD InitCKeyArray(TCascStorage * hs) { size_t nNumberOfFiles = GetEstimatedNumberOfFiles(hs); DWORD dwErrCode; // // Allocate array and map of CKey entries // // Create the array of CKey items dwErrCode = hs->CKeyArray.Create(sizeof(CASC_CKEY_ENTRY), nNumberOfFiles); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Create the map CKey -> CASC_CKEY_ENTRY dwErrCode = hs->CKeyMap.Create(nNumberOfFiles, MD5_HASH_SIZE, FIELD_OFFSET(CASC_CKEY_ENTRY, CKey)); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Create the map CKey -> CASC_CKEY_ENTRY. Note that TVFS root references files // using 9-byte EKey, so cut the search EKey length to 9 bytes dwErrCode = hs->EKeyMap.Create(nNumberOfFiles, CASC_EKEY_SIZE, FIELD_OFFSET(CASC_CKEY_ENTRY, EKey)); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; return ERROR_SUCCESS; } int CaptureEncodingHeader(CASC_ENCODING_HEADER & EnHeader, LPBYTE pbFileData, size_t cbFileData) { PFILE_ENCODING_HEADER pFileHeader = (PFILE_ENCODING_HEADER)pbFileData; // Check the signature ('EN') and version if(cbFileData < sizeof(FILE_ENCODING_HEADER) || pFileHeader->Magic != FILE_MAGIC_ENCODING || pFileHeader->Version != 0x01) return ERROR_BAD_FORMAT; // Note that we don't support CKey and EKey sizes other than 0x10 in the ENCODING file if(pFileHeader->CKeyLength != MD5_HASH_SIZE || pFileHeader->EKeyLength != MD5_HASH_SIZE) return ERROR_BAD_FORMAT; EnHeader.Magic = pFileHeader->Magic; EnHeader.Version = pFileHeader->Version; EnHeader.CKeyLength = pFileHeader->CKeyLength; EnHeader.EKeyLength = pFileHeader->EKeyLength; EnHeader.CKeyPageCount = ConvertBytesToInteger_4(pFileHeader->CKeyPageCount); EnHeader.CKeyPageSize = ConvertBytesToInteger_2(pFileHeader->CKeyPageSize) * 1024; EnHeader.EKeyPageCount = ConvertBytesToInteger_4(pFileHeader->EKeyPageCount); EnHeader.EKeyPageSize = ConvertBytesToInteger_2(pFileHeader->EKeyPageSize) * 1024; EnHeader.ESpecBlockSize = ConvertBytesToInteger_4(pFileHeader->ESpecBlockSize); return ERROR_SUCCESS; } static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHeader, LPBYTE pbPageBegin, LPBYTE pbEndOfPage) { PFILE_CKEY_ENTRY pFileEntry; LPBYTE pbFileEntry = pbPageBegin; // Sanity checks assert(hs->CKeyMap.IsInitialized()); assert(hs->EKeyMap.IsInitialized()); // Parse all encoding entries while(pbFileEntry < pbEndOfPage) { // Get pointer to the encoding entry pFileEntry = (PFILE_CKEY_ENTRY)pbFileEntry; if(pFileEntry->EKeyCount == 0) break; // Example of a file entry with multiple EKeys: // Overwatch build 24919, CKey: 0e 90 94 fa d2 cb 85 ac d0 7c ea 09 f9 c5 ba 00 // BREAKIF(pFileEntry->EKeyCount > 1); // BREAK_ON_XKEY3(pFileEntry->CKey, 0x34, 0x82, 0x1f); // Insert the entry to the central CKey table InsertCKeyEntry(hs, pFileEntry); // Move to the next encoding entry pbFileEntry = pbFileEntry + 2 + 4 + EnHeader.CKeyLength + (pFileEntry->EKeyCount * EnHeader.EKeyLength); } return ERROR_SUCCESS; } static int LoadEncodingManifest(TCascStorage * hs) { CASC_CKEY_ENTRY & CKeyEntry = hs->EncodingCKey; LPBYTE pbEncodingFile; DWORD cbEncodingFile = 0; DWORD dwErrCode = ERROR_SUCCESS; // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Loading ENCODING manifest", NULL, 0, 0)) return ERROR_CANCELLED; // Fill-in the information from the index entry and insert it to the file tree if(!CopyEKeyEntry(hs, &CKeyEntry)) return ERROR_FILE_NOT_FOUND; InsertCKeyEntry(hs, CKeyEntry); // Load the entire encoding file to memory pbEncodingFile = LoadInternalFileToMemory(hs, &hs->EncodingCKey, &cbEncodingFile); if(pbEncodingFile != NULL && cbEncodingFile != 0) { CASC_ENCODING_HEADER EnHeader; // Capture the header of the ENCODING file dwErrCode = CaptureEncodingHeader(EnHeader, pbEncodingFile, cbEncodingFile); if(dwErrCode == ERROR_SUCCESS) { // Get the CKey page header and the first page PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)(pbEncodingFile + sizeof(FILE_ENCODING_HEADER) + EnHeader.ESpecBlockSize); LPBYTE pbCKeyPage = (LPBYTE)(pPageHeader + EnHeader.CKeyPageCount); // Go through all CKey pages and verify them for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++) { // Check if there is enough space in the buffer if((pbCKeyPage + EnHeader.CKeyPageSize) > (pbEncodingFile + cbEncodingFile)) { dwErrCode = ERROR_FILE_CORRUPT; break; } // Check the hash of the entire segment // Note that verifying takes considerable time of the storage loading // if(!VerifyDataBlockHash(pbCKeyPage, EnHeader.CKeyPageSize, pEncodingSegment->SegmentHash)) // { // dwErrCode = ERROR_FILE_CORRUPT; // break; // } // Check if the CKey matches with the expected first value if(memcmp(((PFILE_CKEY_ENTRY)pbCKeyPage)->CKey, pPageHeader[i].FirstKey, MD5_HASH_SIZE)) { dwErrCode = ERROR_FILE_CORRUPT; break; } // Load the entire page of CKey entries. // This operation will never fail, because all memory is already pre-allocated dwErrCode = LoadEncodingCKeyPage(hs, EnHeader, pbCKeyPage, pbCKeyPage + EnHeader.CKeyPageSize); if(dwErrCode != ERROR_SUCCESS) break; // Move to the next CKey page pbCKeyPage += EnHeader.CKeyPageSize; } } // All CKey->EKey entries from the text build files need to be copied to the CKey array if(dwErrCode == ERROR_SUCCESS) { dwErrCode = CopyBuildFileItemsToCKeyArray(hs); } // Free the loaded ENCODING file CASC_FREE(pbEncodingFile); } else { dwErrCode = GetCascError(); } return dwErrCode; } size_t GetTagBitmapLength(LPBYTE pbFilePtr, LPBYTE pbFileEnd, DWORD EntryCount) { size_t nBitmapLength; nBitmapLength = (EntryCount / 8) + ((EntryCount & 0x07) ? 1 : 0); if ((pbFilePtr + nBitmapLength) > pbFileEnd) nBitmapLength = (pbFileEnd - pbFilePtr); return nBitmapLength; } int CaptureDownloadHeader(CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbFileData, size_t cbFileData) { PFILE_DOWNLOAD_HEADER pFileHeader = (PFILE_DOWNLOAD_HEADER)pbFileData; // Check the signature ('DL') and version if(cbFileData < sizeof(FILE_DOWNLOAD_HEADER) || pFileHeader->Magic != FILE_MAGIC_DOWNLOAD || pFileHeader->Version > 3) return ERROR_BAD_FORMAT; // Note that we don't support CKey sizes greater than 0x10 in the DOWNLOAD file if(pFileHeader->EKeyLength > MD5_HASH_SIZE) return ERROR_BAD_FORMAT; // Capture the header version 1 memset(&DlHeader, 0, sizeof(CASC_DOWNLOAD_HEADER)); DlHeader.Magic = pFileHeader->Magic; DlHeader.Version = pFileHeader->Version; DlHeader.EKeyLength = pFileHeader->EKeyLength; DlHeader.EntryHasChecksum = pFileHeader->EntryHasChecksum; DlHeader.EntryCount = ConvertBytesToInteger_4(pFileHeader->EntryCount); DlHeader.TagCount = ConvertBytesToInteger_2(pFileHeader->TagCount); DlHeader.HeaderLength = FIELD_OFFSET(FILE_DOWNLOAD_HEADER, FlagByteSize); DlHeader.EntryLength = DlHeader.EKeyLength + 5 + 1 + (DlHeader.EntryHasChecksum ? 4 : 0); // Capture header version 2 if (pFileHeader->Version >= 2) { DlHeader.FlagByteSize = pFileHeader->FlagByteSize; DlHeader.HeaderLength = FIELD_OFFSET(FILE_DOWNLOAD_HEADER, BasePriority); DlHeader.EntryLength += DlHeader.FlagByteSize; // Capture header version 3 if (pFileHeader->Version >= 3) { DlHeader.BasePriority = pFileHeader->BasePriority; DlHeader.HeaderLength = sizeof(FILE_DOWNLOAD_HEADER); } } return ERROR_SUCCESS; } int CaptureDownloadEntry(CASC_DOWNLOAD_HEADER & DlHeader, CASC_DOWNLOAD_ENTRY & DlEntry, LPBYTE pbFilePtr, LPBYTE pbFileEnd) { // Check the range if((pbFilePtr + DlHeader.EntryLength) >= pbFileEnd) return ERROR_BAD_FORMAT; memset(&DlEntry, 0, sizeof(CASC_DOWNLOAD_ENTRY)); // Copy the EKey memcpy(DlEntry.EKey, pbFilePtr, DlHeader.EKeyLength); pbFilePtr += DlHeader.EKeyLength; // Convert the file size DlEntry.EncodedSize = ConvertBytesToInteger_5(pbFilePtr); pbFilePtr += 5; // Copy the file priority DlEntry.Priority = pbFilePtr[0]; pbFilePtr++; // Copy the checksum if(DlHeader.EntryHasChecksum) { DlEntry.Checksum = ConvertBytesToInteger_4(pbFilePtr); pbFilePtr += 4; } // Copy the flags DlEntry.Flags = ConvertBytesToInteger_X(pbFilePtr, DlHeader.FlagByteSize); return ERROR_SUCCESS; } int CaptureDownloadTag(CASC_DOWNLOAD_HEADER & DlHeader, CASC_TAG_ENTRY1 & DlTag, LPBYTE pbFilePtr, LPBYTE pbFileEnd) { LPBYTE pbSaveFilePtr = pbFilePtr; // Prepare the tag structure memset(&DlTag, 0, sizeof(CASC_TAG_ENTRY1)); DlTag.szTagName = (const char *)pbFilePtr; // Skip the tag string while(pbFilePtr < pbFileEnd && pbFilePtr[0] != 0) pbFilePtr++; if(pbFilePtr >= pbFileEnd) return ERROR_BAD_FORMAT; // Save the length of the tag name DlTag.NameLength = (pbFilePtr - pbSaveFilePtr); pbFilePtr++; // Get the tag value if((pbFilePtr + sizeof(DWORD)) > pbFileEnd) return ERROR_BAD_FORMAT; DlTag.TagValue = ConvertBytesToInteger_2(pbFilePtr); pbFilePtr += 2; // Get the bitmap DlTag.Bitmap = pbFilePtr; // Get the bitmap length. // If the bitmap is last in the list and it's shorter than declared, we make it shorter DlTag.BitmapLength = GetTagBitmapLength(pbFilePtr, pbFileEnd, DlHeader.EntryCount); // Get the entry length DlTag.TagLength = (pbFilePtr - pbSaveFilePtr) + DlTag.BitmapLength; return ERROR_SUCCESS; } static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbFileData, LPBYTE pbFileEnd) { PCASC_TAG_ENTRY1 TagArray = NULL; LPBYTE pbEntries = pbFileData + DlHeader.HeaderLength; LPBYTE pbEntry = pbEntries; LPBYTE pbTags = pbEntries + DlHeader.EntryLength * DlHeader.EntryCount; LPBYTE pbTag = pbTags; size_t nMaxNameLength = 0; size_t nTagEntryLengh = 0; DWORD dwErrCode = ERROR_SUCCESS; // Does the storage support tags? if(DlHeader.TagCount != 0) { // Remember that we support tags hs->dwFeatures |= CASC_FEATURE_TAGS; // Allocate space for the tag array TagArray = CASC_ALLOC(DlHeader.TagCount); if(TagArray != NULL) { // Get the longest tag name for(DWORD i = 0; i < DlHeader.TagCount; i++) { if(CaptureDownloadTag(DlHeader, TagArray[i], pbTag, pbFileEnd) == ERROR_SUCCESS) nMaxNameLength = CASCLIB_MAX(nMaxNameLength, TagArray[i].NameLength); pbTag = pbTag + TagArray[i].TagLength; } // Determine the tag entry length nTagEntryLengh = FIELD_OFFSET(CASC_TAG_ENTRY2, szTagName) + nMaxNameLength; nTagEntryLengh = ALIGN_TO_SIZE(nTagEntryLengh, 8); // Load the tags into array in the storage structure dwErrCode = hs->TagsArray.Create(nTagEntryLengh, DlHeader.TagCount); if(dwErrCode == ERROR_SUCCESS) { // Convert the array of CASC_DOWNLOAD_TAG1 to array of CASC_DOWNLOAD_TAG2 for(DWORD i = 0; i < DlHeader.TagCount; i++) { PCASC_TAG_ENTRY1 pSourceTag = &TagArray[i]; PCASC_TAG_ENTRY2 pTargetTag; // Insert the tag to the array pTargetTag = (PCASC_TAG_ENTRY2)hs->TagsArray.Insert(1); if(pTargetTag == NULL) { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; break; } // Copy the tag structure memset(pTargetTag, 0, nTagEntryLengh); memcpy(pTargetTag->szTagName, pSourceTag->szTagName, pSourceTag->NameLength); pTargetTag->NameLength = pSourceTag->NameLength; pTargetTag->TagValue = pSourceTag->TagValue; } } } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } // Now parse all entries. For each entry, mark the corresponding tag bit in the EKey table for(DWORD i = 0; i < DlHeader.EntryCount; i++) { CASC_DOWNLOAD_ENTRY DlEntry; PCASC_CKEY_ENTRY pCKeyEntry; ULONGLONG TagBit = 1; size_t BitMaskOffset = (i / 8); size_t TagItemCount = hs->TagsArray.ItemCount(); BYTE BitMaskBit = 0x80 >> (i % 8); // Capture the download entry if(CaptureDownloadEntry(DlHeader, DlEntry, pbEntry, pbFileEnd) != ERROR_SUCCESS) break; // COD4: zone/base.xpak //BREAK_ON_XKEY3(DlEntry.EKey, 0xa5, 0x00, 0x16); // Insert the entry to the central CKey table if((pCKeyEntry = InsertCKeyEntry(hs, DlEntry)) != NULL) { // Supply the tag bits for(size_t j = 0; j < TagItemCount; j++) { // Set the bit in the entry, if the tag for it is present if((BitMaskOffset < TagArray[j].BitmapLength) && (TagArray[j].Bitmap[BitMaskOffset] & BitMaskBit)) pCKeyEntry->TagBitMask |= TagBit; // Move to the next bit TagBit <<= 1; } } // Move to the next entry pbEntry += DlHeader.EntryLength; } // Free the tag array, if any CASC_FREE(TagArray); // Remember the total file count hs->TotalFiles = hs->CKeyArray.ItemCount(); return dwErrCode; } static int LoadDownloadManifest(TCascStorage * hs) { PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->DownloadCKey.CKey); LPBYTE pbDownloadFile = NULL; DWORD cbDownloadFile = 0; DWORD dwErrCode = ERROR_SUCCESS; // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Loading DOWNLOAD manifest", NULL, 0, 0)) return ERROR_CANCELLED; // Load the entire DOWNLOAD file to memory pbDownloadFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbDownloadFile); if(pbDownloadFile != NULL && cbDownloadFile != 0) { CASC_DOWNLOAD_HEADER DlHeader; // Capture the header of the DOWNLOAD file dwErrCode = CaptureDownloadHeader(DlHeader, pbDownloadFile, cbDownloadFile); if(dwErrCode == ERROR_SUCCESS) { // Parse the entire download manifest dwErrCode = LoadDownloadManifest(hs, DlHeader, pbDownloadFile, pbDownloadFile + cbDownloadFile); } // Free the loaded manifest CASC_FREE(pbDownloadFile); } // If the DOWNLOAD manifest is not present, we won't abort the downloading process. return dwErrCode; } //----------------------------------------------------------------------------- // INSTALL manifest. This is a replacement for ROOT, if loading ROOT fails // https://wowdev.wiki/TACT#Install_manifest static int LoadInstallManifest(TCascStorage * hs) { PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->InstallCKey.CKey); LPBYTE pbInstallFile = NULL; DWORD cbInstallFile = 0; DWORD dwErrCode = ERROR_SUCCESS; // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Loading INSTALL manifest", NULL, 0, 0)) return ERROR_CANCELLED; // Load the entire DOWNLOAD file to memory pbInstallFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbInstallFile); if (pbInstallFile != NULL && cbInstallFile != 0) { dwErrCode = RootHandler_CreateInstall(hs, pbInstallFile, cbInstallFile); CASC_FREE(pbInstallFile); } else { dwErrCode = GetCascError(); } return dwErrCode; } static bool InsertWellKnownFile(TCascStorage * hs, const char * szFileName, CASC_CKEY_ENTRY & FakeCKeyEntry, DWORD dwFlags = 0) { PCASC_CKEY_ENTRY pCKeyEntry = NULL; // We need to find the CKey entry in the central array if(FakeCKeyEntry.Flags & CASC_CE_HAS_CKEY) { // Did we find anything? pCKeyEntry = FindCKeyEntry_CKey(hs, FakeCKeyEntry.CKey); if(pCKeyEntry != NULL) { // Insert the key to the root handler. Note that the file can already be referenced // ("index" vs "vfs-root" in Warcraft III storages) hs->pRootHandler->Insert(szFileName, pCKeyEntry); // Copy some flags pCKeyEntry->Flags |= (dwFlags | CASC_CE_IN_BUILD); return true; } } // Special case: the PATCH file is usually not in any indices. // It's also never locally available if((dwFlags & CASC_CE_FILE_PATCH) && (hs->dwFeatures & CASC_FEATURE_ONLINE)) { // Get or insert the PATCH entry pCKeyEntry = InsertCKeyEntry(hs, FakeCKeyEntry); if(pCKeyEntry != NULL) { hs->pRootHandler->Insert(szFileName, pCKeyEntry); pCKeyEntry->Flags |= (dwFlags | CASC_CE_IN_BUILD); return true; } } return false; } static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask) { PCASC_CKEY_ENTRY pCKeyEntry; PDWORD FileSignature; LPBYTE pbRootFile = NULL; DWORD cbRootFile = 0; DWORD dwErrCode = ERROR_BAD_FORMAT; // Sanity checks assert(hs->CKeyMap.IsInitialized() == true); assert(hs->pRootHandler == NULL); // Inform the user about what we are doing if(InvokeProgressCallback(hs, "Loading ROOT manifest", NULL, 0, 0)) return ERROR_CANCELLED; // Locale: The default parameter is 0 - in that case, we load all locales dwLocaleMask = (dwLocaleMask != 0) ? dwLocaleMask : 0xFFFFFFFF; // Prioritize the VFS root over legacy ROOT file pCKeyEntry = (hs->VfsRoot.ContentSize != CASC_INVALID_SIZE) ? &hs->VfsRoot : &hs->RootFile; pCKeyEntry = FindCKeyEntry_CKey(hs, pCKeyEntry->CKey); // Load the entire ROOT file to memory pbRootFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbRootFile); if(pbRootFile != NULL) { // Ignore ROOT files that contain just a MD5 hash if(cbRootFile > MD5_STRING_SIZE) { // Check the type of the ROOT file FileSignature = (PDWORD)pbRootFile; switch(FileSignature[0]) { case CASC_MNDX_ROOT_SIGNATURE: dwErrCode = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile); break; case CASC_DIABLO3_ROOT_SIGNATURE: dwErrCode = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile); break; case CASC_TVFS_ROOT_SIGNATURE: dwErrCode = RootHandler_CreateTVFS(hs, pbRootFile, cbRootFile); break; case CASC_WOW82_ROOT_SIGNATURE: dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask); break; default: // // Each of these handler creators must verify their format first. // If the format was not recognized, they need to return ERROR_BAD_FORMAT // dwErrCode = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile); if(dwErrCode == ERROR_BAD_FORMAT) { dwErrCode = RootHandler_CreateStarcraft1(hs, pbRootFile, cbRootFile); if(dwErrCode == ERROR_BAD_FORMAT) { dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask); } } break; } } // Free the root file CASC_FREE(pbRootFile); } else { dwErrCode = GetCascError(); } return dwErrCode; } static DWORD GetStorageTotalFileCount(TCascStorage * hs) { PCASC_CKEY_ENTRY pCKeyEntry; size_t nItemCount = hs->CKeyArray.ItemCount(); DWORD TotalFileCount = 0; for(size_t i = 0; i < nItemCount; i++) { if((pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(i)) != NULL) { if(pCKeyEntry->IsFile()) { // If there is zero or one file name reference, we count the item as one file. // If there is more than 1 name reference, we count the file as many times as number of references DWORD RefCount = (pCKeyEntry->RefCount > 0) ? pCKeyEntry->RefCount : 1; // Add the number of references to the total file count TotalFileCount += RefCount; } } } return TotalFileCount; } static bool GetStorageProduct(TCascStorage * hs, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded) { PCASC_STORAGE_PRODUCT pProductInfo; // Verify whether we have enough space in the buffer pProductInfo = (PCASC_STORAGE_PRODUCT)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, sizeof(CASC_STORAGE_PRODUCT), pcbLengthNeeded); if(pProductInfo != NULL) { // Clear the entire structure memset(pProductInfo, 0, sizeof(CASC_STORAGE_PRODUCT)); // Copy the product code name and build number if(hs->szCodeName != NULL) CascStrCopy(pProductInfo->szCodeName, _countof(pProductInfo->szCodeName), hs->szCodeName); pProductInfo->BuildNumber = hs->dwBuildNumber; } return (pProductInfo != NULL); } static bool GetStorageTags(TCascStorage * hs, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded) { PCASC_STORAGE_TAGS pTags; PCASC_TAG_ENTRY2 pTag; char * szNameBuffer; size_t cbMinLength; // Does the storage support tags? if(hs->TagsArray.IsInitialized() == false) { SetCascError(ERROR_NOT_SUPPORTED); return false; } // Calculate the length of the tags cbMinLength = FIELD_OFFSET(CASC_STORAGE_TAGS, Tags) + hs->TagsArray.ItemCount() * sizeof(CASC_STORAGE_TAG); szNameBuffer = (char *)pvStorageInfo + cbMinLength; // Also include the tag length for(size_t i = 0; i < hs->TagsArray.ItemCount(); i++) { pTag = (PCASC_TAG_ENTRY2)hs->TagsArray.ItemAt(i); cbMinLength = cbMinLength + pTag->NameLength + 1; } // Verify whether we have enough space in the buffer pTags = (PCASC_STORAGE_TAGS)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, cbMinLength, pcbLengthNeeded); if(pTags != NULL) { // Fill the output structure pTags->TagCount = hs->TagsArray.ItemCount(); pTags->Reserved = 0; // Copy the tags for(size_t i = 0; i < hs->TagsArray.ItemCount(); i++) { // Get the source tag pTag = (PCASC_TAG_ENTRY2)hs->TagsArray.ItemAt(i); // Fill the target tag pTags->Tags[i].szTagName = szNameBuffer; pTags->Tags[i].TagNameLength = (DWORD)pTag->NameLength; pTags->Tags[i].TagValue = pTag->TagValue; // Copy the tag name memcpy(szNameBuffer, pTag->szTagName, pTag->NameLength); szNameBuffer[pTag->NameLength] = 0; szNameBuffer = szNameBuffer + pTag->NameLength + 1; } } return (pTags != NULL); } static bool GetStoragePathProduct(TCascStorage * hs, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded) { LPTSTR szBuffer = (LPTSTR)pvStorageInfo; size_t nMaxChars = cbStorageInfo / sizeof(TCHAR); size_t nLength; // Calculate the length needed nLength = _tcslen(hs->szRootPath); if(hs->szCodeName != NULL) nLength = nLength + 1 + _tcslen(hs->szCodeName); if(hs->szRegion != NULL) nLength = nLength + 1 + strlen(hs->szRegion); nLength++; // Verify whether we have enough space in the buffer szBuffer = (LPTSTR)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, (nLength * sizeof(TCHAR)), pcbLengthNeeded); if(szBuffer != NULL) { LPTSTR szBufferEnd = szBuffer + nMaxChars; // Copy the storage path CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szRootPath); szBuffer += _tcslen(hs->szRootPath); // Append the product code name, if any if(hs->szCodeName != NULL) { *szBuffer++ = _T(':'); CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szCodeName); szBuffer += _tcslen(hs->szCodeName); } // Append the product region, if any if(hs->szRegion != NULL) { *szBuffer++ = _T(':'); CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szRegion); } } return (szBuffer != NULL); } static DWORD InitializeLocalDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs) { LPTSTR szWorkPath; DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Find the root directory of the storage. The root directory // is the one with ".build.info" or ".build.db". szWorkPath = CascNewStr(pArgs->szLocalPath); if(szWorkPath != NULL) { // Get the length and go up until we find the ".build.info" or ".build.db" for(;;) { // Is this a game directory? dwErrCode = CheckGameDirectory(hs, szWorkPath); if(dwErrCode == ERROR_SUCCESS) { dwErrCode = ERROR_SUCCESS; break; } // Cut one path part if(!CutLastPathPart(szWorkPath)) { dwErrCode = ERROR_FILE_NOT_FOUND; break; } } // Find the index directory if (dwErrCode == ERROR_SUCCESS) { // First, check for more common "data" subdirectory if ((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL) dwErrCode = ERROR_SUCCESS; // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) else if ((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL) dwErrCode = ERROR_SUCCESS; else dwErrCode = ERROR_FILE_NOT_FOUND; } // Free the work path buffer CASC_FREE(szWorkPath); } return dwErrCode; } static DWORD InitializeOnlineDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs) { // Create the root path hs->szRootPath = CascNewStr(pArgs->szLocalPath); if (hs->szRootPath != NULL) { hs->BuildFileType = CascVersionsDb; hs->dwFeatures |= CASC_FEATURE_ONLINE; return ERROR_SUCCESS; } return ERROR_NOT_ENOUGH_MEMORY; } static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs) { LPCTSTR szCodeName = NULL; LPCTSTR szRegion = NULL; LPCTSTR szBuildKey = NULL; DWORD dwLocaleMask = 0; DWORD dwErrCode = ERROR_SUCCESS; // Pass the argument array to the storage hs->pArgs = pArgs; // Extract optional arguments ExtractVersionedArgument(pArgs, FIELD_OFFSET(CASC_OPEN_STORAGE_ARGS, dwLocaleMask), &dwLocaleMask); // Extract the product code name if(ExtractVersionedArgument(pArgs, FIELD_OFFSET(CASC_OPEN_STORAGE_ARGS, szCodeName), &szCodeName) && szCodeName != NULL) hs->szCodeName = CascNewStr(szCodeName); // Extract the region (optional) if(ExtractVersionedArgument(pArgs, FIELD_OFFSET(CASC_OPEN_STORAGE_ARGS, szRegion), &szRegion) && szRegion != NULL) hs->szRegion = CascNewStrT2A(szRegion); // Extract the build key (optional) if(ExtractVersionedArgument(pArgs, FIELD_OFFSET(CASC_OPEN_STORAGE_ARGS, szBuildKey), &szBuildKey) && szBuildKey != NULL) hs->szBuildKey = CascNewStrT2A(szBuildKey); // Special handling to online storages if(hs->dwFeatures & CASC_FEATURE_ONLINE) { // Enable caching of the sockets. This will add references // to all existing and all future sockets sockets_set_caching(true); // For online storages, we need to load CDN servers dwErrCode = LoadCdnsFile(hs); } // Now, load the main storage file ".build.info" (or ".build.db" in old storages) if(dwErrCode == ERROR_SUCCESS) { dwErrCode = LoadBuildInfo(hs); } // If the .build.info OR .build.db file has been loaded, // proceed with loading the CDN config file if (dwErrCode == ERROR_SUCCESS) { dwErrCode = LoadCdnConfigFile(hs); if(dwErrCode != ERROR_SUCCESS && (hs->dwFeatures & CASC_FEATURE_ONLINE) == 0) dwErrCode = ERROR_SUCCESS; } // Proceed with loading the CDN build file if (dwErrCode == ERROR_SUCCESS) { dwErrCode = LoadCdnBuildFile(hs); } // Create the array of CKey entries. Each entry represents a file in the storage if(dwErrCode == ERROR_SUCCESS) { dwErrCode = InitCKeyArray(hs); } // Pre-load the local index files if(dwErrCode == ERROR_SUCCESS) { dwErrCode = LoadIndexFiles(hs); } // Load the ENCODING manifest if(dwErrCode == ERROR_SUCCESS) { dwErrCode = LoadEncodingManifest(hs); } // We need to load the DOWNLOAD manifest if(dwErrCode == ERROR_SUCCESS) { dwErrCode = LoadDownloadManifest(hs); } // Load the build manifest ("ROOT" file) if(dwErrCode == ERROR_SUCCESS) { // For WoW storages, multiple files are present in the storage (same name, same file data ID, different locale). // Failing to select storage on them will lead to the first-in-order file in the list being loaded. // Example: WoW build 32144, file: DBFilesClient\Achievement.db2, file data ID: 1260179 // Locales: koKR frFR deDE zhCN esES zhTW enUS&enGB esMX ruRU itIT ptBT&ptPT (in order of appearance in the build manifest) if(dwLocaleMask == 0) { dwLocaleMask = hs->dwDefaultLocale; } // Continue loading the manifest dwErrCode = LoadBuildManifest(hs, dwLocaleMask); if (dwErrCode != ERROR_SUCCESS) { // If we fail to load the ROOT file, we take the file names from the INSTALL manifest dwErrCode = LoadInstallManifest(hs); } } // Insert entries for files with well-known names. Their CKeys are in the BUILD file // See https://wowdev.wiki/TACT#Encoding_table for their list if (dwErrCode == ERROR_SUCCESS) { InsertWellKnownFile(hs, "ENCODING", hs->EncodingCKey); InsertWellKnownFile(hs, "DOWNLOAD", hs->DownloadCKey); InsertWellKnownFile(hs, "INSTALL", hs->InstallCKey); InsertWellKnownFile(hs, "PATCH", hs->PatchFile, CASC_CE_FILE_PATCH); InsertWellKnownFile(hs, "ROOT", hs->RootFile); InsertWellKnownFile(hs, "SIZE", hs->SizeFile); // Also reset the total file count. CascGetStorageInfo will update it on next call hs->TotalFiles = 0; } // Load the encryption keys if (dwErrCode == ERROR_SUCCESS) { dwErrCode = CascLoadEncryptionKeys(hs); } // Cleanup and exit FreeIndexFiles(hs); hs->pArgs = NULL; return dwErrCode; } static LPTSTR ParseOpenParams(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs) { LPTSTR szParamsCopy; // The 'szParams' must not be empty if(szParams == NULL || pArgs == NULL || szParams[0] == 0) { SetCascError(ERROR_INVALID_PARAMETER); return NULL; } // The 'pArgs' must be valid but must not contain 'szLocalPath', 'szCodeName' or 'szRegion' if(pArgs->szLocalPath != NULL || pArgs->szCodeName != NULL || pArgs->szRegion != NULL) { SetCascError(ERROR_INVALID_PARAMETER); return NULL; } // Make a copy of the parameters so we can temper with them if((szParamsCopy = CascNewStr(szParams)) != NULL) { LPTSTR szPlainName = (LPTSTR)GetPlainFileName(szParamsCopy); LPTSTR szSeparator; // The local path is always set pArgs->szLocalPath = szParamsCopy; pArgs->szCodeName = NULL; pArgs->szRegion = NULL; pArgs->szBuildKey = NULL; // Find the first ":". This will indicate the end of local path and also begin of product code if((szSeparator = _tcschr(szPlainName, _T(':'))) != NULL) { // The found string is a product code name pArgs->szCodeName = szSeparator + 1; szSeparator[0] = 0; // Try again. If found, it is a product region if((szSeparator = _tcschr(szSeparator + 1, _T(':'))) != NULL) { pArgs->szRegion = szSeparator + 1; szSeparator[0] = 0; // Try again. If found, it is a build key (MD5 of a build file) if((szSeparator = _tcschr(szSeparator + 1, _T(':'))) != NULL) { pArgs->szBuildKey = szSeparator + 1; szSeparator[0] = 0; } } } } else { SetCascError(ERROR_NOT_ENOUGH_MEMORY); } return szParamsCopy; } //----------------------------------------------------------------------------- // Public functions bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, bool bOnlineStorage, HANDLE * phStorage) { CASC_OPEN_STORAGE_ARGS LocalArgs = {sizeof(CASC_OPEN_STORAGE_ARGS)}; TCascStorage * hs; LPTSTR szParamsCopy = NULL; DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // The storage path[+product[+region]] must either be passed in szParams or in pArgs. Not both. // It is allowed to pass NULL as pArgs if the szParams is not NULL if(szParams != NULL) { if(pArgs == NULL) pArgs = &LocalArgs; szParamsCopy = ParseOpenParams(szParams, pArgs); if(szParamsCopy == NULL) return false; } else { // The arguments and the local path must be entered if(pArgs == NULL || pArgs->szLocalPath == NULL || pArgs->szLocalPath[0] == 0) { SetCascError(ERROR_INVALID_PARAMETER); return false; } } // Allocate the storage structure if((hs = new TCascStorage()) != NULL) { // Setup the directories dwErrCode = (bOnlineStorage) ? InitializeOnlineDirectories(hs, pArgs) : InitializeLocalDirectories(hs, pArgs); if(dwErrCode == ERROR_SUCCESS) { // Perform the entire storage loading dwErrCode = LoadCascStorage(hs, pArgs); } // Free the storage structure on fail if(dwErrCode != ERROR_SUCCESS) { hs = hs->Release(); } } // Give the output parameter to the caller CASC_FREE(szParamsCopy); *phStorage = (HANDLE)hs; // Return the result if(dwErrCode != ERROR_SUCCESS) SetCascError(dwErrCode); return (dwErrCode == ERROR_SUCCESS); } // szParams: "LocalPath:CodeName", e.g. "C:\\Games\\World of Warcraft:wowt" // * LocalPath: Local folder, where the online file will be cached. // * CodeName: Product code name, e.g. "agent" for Battle.net Agent. More info: https://wowdev.wiki/TACT#Products bool WINAPI CascOpenStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage) { CASC_OPEN_STORAGE_ARGS OpenArgs = {sizeof(CASC_OPEN_STORAGE_ARGS)}; OpenArgs.dwLocaleMask = dwLocaleMask; return CascOpenStorageEx(szParams, &OpenArgs, false, phStorage); } // Allows to browse an online CDN storage // szParams: "CachePath:CodeName:Region", e.g. "C:\\Cache:wowt:us" // * CachePath: Local folder, where the online file will be cached. // * CodeName: Product code name, e.g. "agent" for Battle.net Agent. More info: https://wowdev.wiki/TACT#Products // * Region: The region (or subvariant) of the product. Corresponds to the first column of the "versions" file. bool WINAPI CascOpenOnlineStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage) { CASC_OPEN_STORAGE_ARGS OpenArgs = {sizeof(CASC_OPEN_STORAGE_ARGS)}; OpenArgs.dwLocaleMask = dwLocaleMask; return CascOpenStorageEx(szParams, &OpenArgs, true, phStorage); } bool WINAPI CascGetStorageInfo( HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded) { TCascStorage * hs; PDWORD PtrOutputValue; DWORD dwInfoValue = 0; // Verify the storage handle hs = TCascStorage::IsValid(hStorage); if(hs == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // Differentiate between info classes switch(InfoClass) { case CascStorageLocalFileCount: dwInfoValue = (DWORD)hs->LocalFiles; break; case CascStorageTotalFileCount: if(hs->TotalFiles == 0) hs->TotalFiles = GetStorageTotalFileCount(hs); dwInfoValue = (DWORD)hs->TotalFiles; break; case CascStorageFeatures: dwInfoValue = hs->dwFeatures | hs->pRootHandler->GetFeatures(); break; case CascStorageInstalledLocales: dwInfoValue = hs->dwDefaultLocale; break; case CascStorageProduct: return GetStorageProduct(hs, pvStorageInfo, cbStorageInfo, pcbLengthNeeded); case CascStorageTags: return GetStorageTags(hs, pvStorageInfo, cbStorageInfo, pcbLengthNeeded); case CascStoragePathProduct: return GetStoragePathProduct(hs, pvStorageInfo, cbStorageInfo, pcbLengthNeeded); default: SetCascError(ERROR_INVALID_PARAMETER); return false; } // // Default: return a 32-bit unsigned value // PtrOutputValue = (PDWORD)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, sizeof(DWORD), pcbLengthNeeded); if(PtrOutputValue != NULL) PtrOutputValue[0] = dwInfoValue; return (PtrOutputValue != NULL); } bool WINAPI CascCloseStorage(HANDLE hStorage) { TCascStorage * hs; // Verify the storage handle hs = TCascStorage::IsValid(hStorage); if(hs == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Only free the storage if the reference count reaches 0 hs->Release(); return true; } ================================================ FILE: deps/CascLib/src/CascPort.h ================================================ /*****************************************************************************/ /* CascPort.h Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Portability module for the CascLib library. Contains a wrapper symbols */ /* to make the compilation under Linux work */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad Created */ /*****************************************************************************/ #ifndef __CASCPORT_H__ #define __CASCPORT_H__ #ifndef __cplusplus #include #endif //----------------------------------------------------------------------------- // Defines for Windows #if !defined(CASCLIB_PLATFORM_DEFINED) && (defined(_WIN32) || defined(_WIN64)) // In MSVC 8.0, there are some functions declared as deprecated. #define _CRT_SECURE_NO_DEPRECATE #define _CRT_NON_CONFORMING_SWPRINTFS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif // Suppress definitions of `min` and `max` macros by : #define NOMINMAX 1 #include #include #include #include #include #include #include #include #include #include #include #define CASCLIB_PLATFORM_LITTLE_ENDIAN #pragma intrinsic(memset, memcmp, memcpy) // Make these functions intrinsic (inline) #define URL_SEP_CHAR '/' #define PATH_SEP_CHAR '\\' #define PATH_SEP_STRING "\\" #define CASCLIB_PLATFORM_WINDOWS #define CASCLIB_PLATFORM_DEFINED // The platform is known now #endif #ifndef FIELD_OFFSET #define FIELD_OFFSET(type, field) ((LONG)(size_t)&(((type *)0)->field)) #endif //----------------------------------------------------------------------------- // Defines for Mac #if !defined(CASCLIB_PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API // Macintosh #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Support for PowerPC on Max OS X #if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1) #include #include #endif #define PKEXPORT #ifndef __SYS_ZLIB #define __SYS_ZLIB #endif #ifndef __BIG_ENDIAN__ #define CASCLIB_PLATFORM_LITTLE_ENDIAN #endif #define URL_SEP_CHAR '/' #define PATH_SEP_CHAR '/' #define PATH_SEP_STRING "/" typedef int SOCKET; #define CASCLIB_PLATFORM_MAC #define CASCLIB_PLATFORM_DEFINED // The platform is known now #endif //----------------------------------------------------------------------------- // Assumption: we are not on Windows nor Macintosh, so this must be linux *grin* #if !defined(CASCLIB_PLATFORM_DEFINED) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define URL_SEP_CHAR '/' #define PATH_SEP_CHAR '/' #define PATH_SEP_STRING "/" typedef int SOCKET; #define CASCLIB_PLATFORM_LITTLE_ENDIAN #define CASCLIB_PLATFORM_LINUX #define CASCLIB_PLATFORM_DEFINED #endif //----------------------------------------------------------------------------- // Definition of Windows-specific types for non-Windows platforms #ifndef CASCLIB_PLATFORM_WINDOWS // Typedefs for ANSI C typedef unsigned char BYTE; typedef unsigned short USHORT; typedef int LONG; typedef unsigned int DWORD; typedef long long LONGLONG; typedef signed long long LONGLONG; typedef signed long long *PLONGLONG; typedef unsigned long long ULONGLONG; typedef unsigned long long *PULONGLONG; typedef void * HANDLE; typedef char TCHAR; typedef unsigned int LCID; typedef LONG * PLONG; typedef DWORD * PDWORD; typedef BYTE * LPBYTE; typedef char * LPSTR; typedef const char * LPCSTR; typedef TCHAR * LPTSTR; typedef const TCHAR * LPCTSTR; #ifndef __LP64__ #define _LZMA_UINT32_IS_ULONG #endif // Some Windows-specific defines #ifndef MAX_PATH #define MAX_PATH 1024 #endif #define WINAPI #define FILE_BEGIN SEEK_SET #define FILE_CURRENT SEEK_CUR #define FILE_END SEEK_END #define INVALID_HANDLE_VALUE ((HANDLE)-1) #define _T(x) x #define _tcslen strlen #define _tcscat strcat #define _tcschr strchr #define _tcsrchr strrchr #define _tcsstr strstr #define _tcsspn strspn #define _tcsncmp strncmp #define _tprintf printf #define _tremove remove #define _taccess access #define _access access #define _stricmp strcasecmp #define _strnicmp strncasecmp #define _tcsicmp strcasecmp #define _tcsnicmp strncasecmp #define closesocket close #endif // !CASCLIB_PLATFORM_WINDOWS // 64-bit calls are supplied by "normal" calls on Mac #if defined(CASCLIB_PLATFORM_MAC) #define stat64 stat #define fstat64 fstat #define lseek64 lseek #define ftruncate64 ftruncate #define off64_t off_t #define O_LARGEFILE 0 #endif // Platform-specific error codes for UNIX-based platforms #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) #define ERROR_SUCCESS 0 #define ERROR_FILE_NOT_FOUND ENOENT #define ERROR_PATH_NOT_FOUND ENOENT #define ERROR_ACCESS_DENIED EPERM #define ERROR_INVALID_HANDLE EBADF #define ERROR_NOT_ENOUGH_MEMORY ENOMEM #define ERROR_NOT_SUPPORTED ENOTSUP #define ERROR_INVALID_PARAMETER EINVAL #define ERROR_DISK_FULL ENOSPC #define ERROR_ALREADY_EXISTS EEXIST #define ERROR_INSUFFICIENT_BUFFER ENOBUFS #define ERROR_BAD_FORMAT 1000 // No such error code under Linux #define ERROR_NO_MORE_FILES 1001 // No such error code under Linux #define ERROR_HANDLE_EOF 1002 // No such error code under Linux #define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux #define ERROR_FILE_CORRUPT 1004 // No such error code under Linux #define ERROR_FILE_ENCRYPTED 1005 // Returned by encrypted stream when can't find file key #define ERROR_FILE_TOO_LARGE 1006 // No such error code under Linux #define ERROR_ARITHMETIC_OVERFLOW 1007 // The string value is too large to fit in the given type #define ERROR_NETWORK_NOT_AVAILABLE 1008 // Cannot connect to the internet #endif #ifndef ERROR_FILE_INCOMPLETE #define ERROR_FILE_INCOMPLETE 1006 // The required file part is missing #endif #ifndef ERROR_FILE_OFFLINE #define ERROR_FILE_OFFLINE 1007 // The file is not available in the local storage #endif #ifndef ERROR_BUFFER_OVERFLOW #define ERROR_BUFFER_OVERFLOW 1008 #endif #ifndef ERROR_CANCELLED #define ERROR_CANCELLED 1009 #endif #ifndef ERROR_INDEX_PARSING_DONE #define ERROR_INDEX_PARSING_DONE 1010 #endif #ifndef _countof #define _countof(x) (sizeof(x) / sizeof(x[0])) #endif //----------------------------------------------------------------------------- // Swapping functions #ifdef CASCLIB_PLATFORM_LITTLE_ENDIAN #define BSWAP_INT16_UNSIGNED(a) (a) #define BSWAP_INT16_SIGNED(a) (a) #define BSWAP_INT32_UNSIGNED(a) (a) #define BSWAP_INT32_SIGNED(a) (a) #define BSWAP_INT64_SIGNED(a) (a) #define BSWAP_INT64_UNSIGNED(a) (a) #define BSWAP_ARRAY16_UNSIGNED(a,b) {} #define BSWAP_ARRAY32_UNSIGNED(a,b) {} #define BSWAP_ARRAY64_UNSIGNED(a,b) {} #else #ifdef __cplusplus extern "C" { #endif int16_t SwapInt16(uint16_t); uint16_t SwapUInt16(uint16_t); int32_t SwapInt32(uint32_t); uint32_t SwapUInt32(uint32_t); int64_t SwapInt64(uint64_t); uint64_t SwapUInt64(uint64_t); void ConvertUInt16Buffer(void * ptr, size_t length); void ConvertUInt32Buffer(void * ptr, size_t length); void ConvertUInt64Buffer(void * ptr, size_t length); #ifdef __cplusplus } #endif #define BSWAP_INT16_SIGNED(a) SwapInt16((a)) #define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a)) #define BSWAP_INT32_SIGNED(a) SwapInt32((a)) #define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a)) #define BSWAP_INT64_SIGNED(a) SwapInt64((a)) #define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a)) #define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b)) #define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b)) #define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b)) #endif //----------------------------------------------------------------------------- // Interlocked operations inline DWORD CascInterlockedIncrement(DWORD * PtrValue) { #ifdef CASCLIB_PLATFORM_WINDOWS return (DWORD)InterlockedIncrement((LONG *)(PtrValue)); #elif defined(__GNUC__) return __sync_add_and_fetch(PtrValue, 1); #else #define INTERLOCKED_NOT_SUPPORTED return ++(*PtrValue); #endif } inline DWORD CascInterlockedDecrement(DWORD * PtrValue) { #ifdef CASCLIB_PLATFORM_WINDOWS return (DWORD)InterlockedDecrement((LONG *)(PtrValue)); #elif defined(__GNUC__) return __sync_sub_and_fetch(PtrValue, 1); #else return --(*PtrValue); #endif } //----------------------------------------------------------------------------- // Lock functions #ifdef CASCLIB_PLATFORM_WINDOWS typedef RTL_CRITICAL_SECTION CASC_LOCK; #define CascInitLock(Lock) InitializeCriticalSection(&Lock); #define CascFreeLock(Lock) DeleteCriticalSection(&Lock); #define CascLock(Lock) EnterCriticalSection(&Lock); #define CascUnlock(Lock) LeaveCriticalSection(&Lock); #else typedef pthread_mutex_t CASC_LOCK; #define CascInitLock(Lock) pthread_mutex_init(&Lock, NULL); #define CascFreeLock(Lock) pthread_mutex_destroy(&Lock); #define CascLock(Lock) pthread_mutex_lock(&Lock); #define CascUnlock(Lock) pthread_mutex_unlock(&Lock); #endif //----------------------------------------------------------------------------- // Forbidden functions, do not use #ifdef __CASCLIB_SELF__ #define strcpy UNSAFE_DO_NOT_USE_STRCPY #define strcat UNSAFE_DO_NOT_USE_STRCAT #define sprintf UNSAFE_DO_NOT_USE_SPRINTF #endif #endif // __CASCPORT_H__ ================================================ FILE: deps/CascLib/src/CascReadFile.cpp ================================================ /****************************************************************************/ /* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* System-dependent directory functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 01.05.14 1.00 Lad The first version of CascOpenFile.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Local functions static DWORD GetStreamEncodedSize(TFileStream * pStream) { ULONGLONG FileSize = 0; FileStream_GetSize(pStream, &FileSize); assert((FileSize >> 32) == 0); return (DWORD)(FileSize); } static DWORD OpenDataStream(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry, bool bDownloadFileIf) { TCascStorage * hs = hf->hs; TFileStream * pStream = NULL; TCHAR szCachePath[MAX_PATH]; TCHAR szDataFile[MAX_PATH]; TCHAR szPlainName[0x80]; DWORD dwErrCode; // If the file is available locally, we rely on data files. // If not, we download the file and open the stream if(pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL) { DWORD dwArchiveIndex = pFileSpan->ArchiveIndex; // Lock the storage to make the operation thread-safe CascLock(hs->StorageLock); // If the data archive is not open yet, open it now. if(hs->DataFiles[dwArchiveIndex] == NULL) { // Prepare the name of the data file CascStrPrintf(szPlainName, _countof(szPlainName), _T("data.%03u"), dwArchiveIndex); CombinePath(szDataFile, _countof(szDataFile), hs->szIndexPath, szPlainName, NULL); // Open the data stream with read+write sharing to prevent Battle.net agent // detecting a corruption and redownloading the entire package pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | STREAM_FLAG_FILL_MISSING | BASE_PROVIDER_FILE); hs->DataFiles[dwArchiveIndex] = pStream; } // Unlock the storage CascUnlock(hs->StorageLock); // Return error or success pFileSpan->pStream = hs->DataFiles[dwArchiveIndex]; return (pFileSpan->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; } else { if(bDownloadFileIf) { CASC_CDN_DOWNLOAD CdnsInfo = {0}; LPCTSTR szPathType = (pCKeyEntry->Flags & CASC_CE_FILE_PATCH) ? _T("patch") : _T("data"); // Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file CdnsInfo.szCdnsPath = hs->szCdnPath; CdnsInfo.szPathType = szPathType; CdnsInfo.pbEKey = pCKeyEntry->EKey; CdnsInfo.szLocalPath = szCachePath; CdnsInfo.ccLocalPath = _countof(szCachePath); // Download the file from CDN dwErrCode = DownloadFileFromCDN(hs, CdnsInfo); if(dwErrCode == ERROR_SUCCESS) { pStream = FileStream_OpenFile(szCachePath, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT); if(pStream != NULL) { // Initialize information about the position and size of the file in archive // On loose files, their position is zero and encoded size is length of the file if(CdnsInfo.pbArchiveKey != NULL) { // Archive position pFileSpan->ArchiveIndex = CdnsInfo.ArchiveIndex; pFileSpan->ArchiveOffs = (DWORD)CdnsInfo.ArchiveOffs; // Encoded size if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE) pCKeyEntry->EncodedSize = CdnsInfo.EncodedSize; assert(pCKeyEntry->EncodedSize == CdnsInfo.EncodedSize); } else { // Archive position pFileSpan->ArchiveIndex = 0; pFileSpan->ArchiveOffs = 0; // Encoded size if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE) pCKeyEntry->EncodedSize = GetStreamEncodedSize(pStream); assert(pCKeyEntry->EncodedSize == GetStreamEncodedSize(pStream)); } // We need to close the file stream after we're done pFileSpan->pStream = pStream; hf->bCloseFileStream = true; return ERROR_SUCCESS; } } } return ERROR_FILE_OFFLINE; } } #ifdef _DEBUG static unsigned int table_16C57A8[0x10] = { 0x049396B8, 0x72A82A9B, 0xEE626CCA, 0x9917754F, 0x15DE40B1, 0xF5A8A9B6, 0x421EAC7E, 0xA9D55C9A, 0x317FD40C, 0x04FAF80D, 0x3D6BE971, 0x52933CFD, 0x27F64B7D, 0xC6F5C11B, 0xD5757E3A, 0x6C388745 }; // Obtained from Agent.exe v 2.15.0.6296 (d14ec9d9a1b396a42964b05f40ea55f37eae5478d550c07ebb6cb09e50968d62) // Note the "Checksum" value probably won't match with older game versions. static void VerifyHeaderSpan(PBLTE_ENCODED_HEADER pBlteHeader, ULONGLONG HeaderOffset) { LPBYTE pbBlteHeader = (LPBYTE)pBlteHeader; DWORD dwInt32; BYTE EncodedOffset[4] = { 0 }; BYTE HashedHeader[4] = { 0 }; BYTE JenkinsHash[4]; BYTE Checksum[4]; size_t i, j; // Seems to be hardcoded to zero assert(pBlteHeader->field_15 == 0); // Calculate the Jenkins hash and write it to the header dwInt32 = hashlittle(pbBlteHeader, FIELD_OFFSET(BLTE_ENCODED_HEADER, JenkinsHash), 0x3D6BE971); ConvertIntegerToBytes_4_LE(dwInt32, JenkinsHash); // assert(memcmp(pBlteHeader->JenkinsHash, JenkinsHash, sizeof(JenkinsHash)) == 0); // Encode the lower 32-bits of the offset dwInt32 = (DWORD)(HeaderOffset + FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature)); dwInt32 = table_16C57A8[dwInt32 & 0x0F] ^ dwInt32; ConvertIntegerToBytes_4_LE(dwInt32, EncodedOffset); // Calculate checksum of the so-far filled structure for (i = 0; i < FIELD_OFFSET(BLTE_ENCODED_HEADER, Checksum); i++) HashedHeader[i & 3] ^= pbBlteHeader[i]; // XOR the two values together to get the final checksum. for (j = 0; j < 4; j++, i++) Checksum[j] = HashedHeader[i & 3] ^ EncodedOffset[i & 3]; // assert(memcmp(pBlteHeader->Checksum, Checksum, sizeof(Checksum)) == 0); } #endif static DWORD ParseBlteHeader(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG HeaderOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t * pcbHeaderSize) { PBLTE_ENCODED_HEADER pEncodedHeader = (PBLTE_ENCODED_HEADER)pbEncodedBuffer; PBLTE_HEADER pBlteHeader = (PBLTE_HEADER)pbEncodedBuffer; DWORD ExpectedHeaderSize; DWORD ExHeaderSize = 0; DWORD HeaderSize; DWORD FrameCount = 0; CASCLIB_UNUSED(HeaderOffset); // On files within storage segments ("data.###"), there is BLTE_ENCODED_HEADER // On local files, there is just PBLTE_HEADER if(ConvertBytesToInteger_4_LE(pBlteHeader->Signature) != BLTE_HEADER_SIGNATURE) { // There must be at least some bytes if (cbEncodedBuffer < FIELD_OFFSET(BLTE_ENCODED_HEADER, MustBe0F)) return ERROR_BAD_FORMAT; if (pEncodedHeader->EncodedSize != pCKeyEntry->EncodedSize) return ERROR_BAD_FORMAT; #ifdef _DEBUG // Not really needed, it's here just for explanation of what the values mean //assert(memcmp(pCKeyEntry->EKey, pEncodedHeader->EKey.Value, MD5_HASH_SIZE) == 0); VerifyHeaderSpan(pEncodedHeader, HeaderOffset); #endif // Capture the EKey ExHeaderSize = FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature); pBlteHeader = (PBLTE_HEADER)(pbEncodedBuffer + ExHeaderSize); } // Verify the signature if(ConvertBytesToInteger_4_LE(pBlteHeader->Signature) != BLTE_HEADER_SIGNATURE) return ERROR_BAD_FORMAT; // Capture the header size. If this is non-zero, then array // of chunk headers follow. Otherwise, the file is just one chunk HeaderSize = ConvertBytesToInteger_4(pBlteHeader->HeaderSize); if (HeaderSize != 0) { if (pBlteHeader->MustBe0F != 0x0F) return ERROR_BAD_FORMAT; // Verify the header size FrameCount = ConvertBytesToInteger_3(pBlteHeader->FrameCount); ExpectedHeaderSize = 0x0C + FrameCount * sizeof(BLTE_FRAME); if (ExpectedHeaderSize != HeaderSize) return ERROR_BAD_FORMAT; // Give the values pcbHeaderSize[0] = ExHeaderSize + FIELD_OFFSET(BLTE_HEADER, MustBe0F) + sizeof(DWORD); } else { pcbHeaderSize[0] = ExHeaderSize + FIELD_OFFSET(BLTE_HEADER, MustBe0F); } // Give the frame count pFileSpan->FrameCount = FrameCount; return ERROR_SUCCESS; } static LPBYTE ReadMissingHeaderData(PCASC_FILE_SPAN pFileSpan, ULONGLONG DataFileOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t cbTotalHeaderSize) { LPBYTE pbNewBuffer; // Reallocate the buffer pbNewBuffer = CASC_REALLOC(BYTE, pbEncodedBuffer, cbTotalHeaderSize); if (pbNewBuffer != NULL) { // Load the missing data DataFileOffset += cbEncodedBuffer; if (FileStream_Read(pFileSpan->pStream, &DataFileOffset, pbNewBuffer + cbEncodedBuffer, (DWORD)(cbTotalHeaderSize - cbEncodedBuffer))) { return pbNewBuffer; } } // If anything failed, we free the original buffer and return NULL; CASC_FREE(pbEncodedBuffer); return NULL; } static LPBYTE CaptureBlteFileFrame(CASC_FILE_FRAME & Frame, LPBYTE pbFramePtr, LPBYTE pbFrameEnd) { PBLTE_FRAME pFileFrame = (PBLTE_FRAME)pbFramePtr; // Check whether we have enough data ready if((pbFramePtr + sizeof(BLTE_FRAME)) > pbFrameEnd) return NULL; Frame.FrameHash = pFileFrame->FrameHash; Frame.ContentSize = ConvertBytesToInteger_4(pFileFrame->ContentSize); Frame.EncodedSize = ConvertBytesToInteger_4(pFileFrame->EncodedSize); return pbFramePtr + sizeof(BLTE_FRAME); } static DWORD LoadSpanFrames(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG DataFileOffset, LPBYTE pbFramePtr, LPBYTE pbFrameEnd, size_t cbHeaderSize) { PCASC_FILE_FRAME pFrames = NULL; DWORD ContentSize = 0; DWORD dwErrCode = ERROR_SUCCESS; assert(pFileSpan != NULL); assert(pFileSpan->pStream != NULL); assert(pFileSpan->pFrames == NULL); if (pFileSpan->FrameCount != 0) { // Move the raw archive offset DataFileOffset += (pFileSpan->FrameCount * sizeof(BLTE_FRAME)); // Allocate array of file frames pFrames = CASC_ALLOC(pFileSpan->FrameCount); if (pFrames != NULL) { // Copy the frames to the file structure for (DWORD i = 0; i < pFileSpan->FrameCount; i++) { CASC_FILE_FRAME & Frame = pFrames[i]; // Capture the single BLTE frame pbFramePtr = CaptureBlteFileFrame(Frame, pbFramePtr, pbFrameEnd); if(pbFramePtr == NULL) { dwErrCode = ERROR_BAD_FORMAT; break; } // Fill-in the file range of the frame Frame.StartOffset = pFileSpan->StartOffset + ContentSize; Frame.EndOffset = Frame.StartOffset + Frame.ContentSize; ContentSize += Frame.ContentSize; // Fill-in the archive range of the frame assert((DataFileOffset + Frame.EncodedSize) > DataFileOffset); Frame.DataFileOffset = DataFileOffset; DataFileOffset += Frame.EncodedSize; } // Save the content size of the file if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE) { pCKeyEntry->ContentSize = ContentSize; } } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } else { // Allocate single "dummy" frame pFrames = CASC_ALLOC(1); if (pFrames != NULL) { // Fill the single frame memset(&pFrames->FrameHash, 0, sizeof(CONTENT_KEY)); pFrames->StartOffset = pFileSpan->StartOffset; pFrames->EndOffset = pFileSpan->EndOffset; pFrames->DataFileOffset = DataFileOffset; pFrames->EncodedSize = (DWORD)(pCKeyEntry->EncodedSize - cbHeaderSize); pFrames->ContentSize = pCKeyEntry->ContentSize; // Save the number of file frames pFileSpan->FrameCount = 1; } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } // Free the frame array on error if(dwErrCode != ERROR_SUCCESS) { pFileSpan->FrameCount = 0; CASC_FREE(pFrames); } pFileSpan->pFrames = pFrames; return dwErrCode; } static DWORD LoadSpanFramesForPlainFile(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry) { PCASC_FILE_FRAME pFrames; // Allocate single "dummy" frame pFrames = CASC_ALLOC(1); if (pFrames != NULL) { // Setup the size pFileSpan->EndOffset = pFileSpan->StartOffset + pCKeyEntry->ContentSize; pCKeyEntry->Flags |= CASC_CE_PLAIN_DATA; // Fill the single frame memset(&pFrames->FrameHash, 0, sizeof(CONTENT_KEY)); pFrames->StartOffset = pFileSpan->StartOffset; pFrames->EndOffset = pFrames->StartOffset + pCKeyEntry->ContentSize; pFrames->DataFileOffset = 0; pFrames->EncodedSize = pCKeyEntry->EncodedSize; pFrames->ContentSize = pCKeyEntry->ContentSize; // Save the number of file frames pFileSpan->FrameCount = 1; pFileSpan->pFrames = pFrames; return ERROR_SUCCESS; } return ERROR_NOT_ENOUGH_MEMORY; } static DWORD LoadEncodedHeaderAndSpanFrames(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry) { LPBYTE pbEncodedBuffer; size_t cbEncodedBuffer = MAX_ENCODED_HEADER; DWORD dwErrCode = ERROR_SUCCESS; // Should only be called when the file frames are NOT loaded assert(pFileSpan->pFrames == NULL); assert(pFileSpan->FrameCount == 0); // Allocate the initial buffer for the encoded headers pbEncodedBuffer = CASC_ALLOC(MAX_ENCODED_HEADER); if (pbEncodedBuffer != NULL) { ULONGLONG ReadOffset = pFileSpan->ArchiveOffs; size_t cbTotalHeaderSize; size_t cbHeaderSize = 0; // At this point, we expect encoded size to be known assert(pCKeyEntry->EncodedSize != CASC_INVALID_SIZE); // Do not read more than encoded size cbEncodedBuffer = CASCLIB_MIN(cbEncodedBuffer, pCKeyEntry->EncodedSize); // Load the entire (eventual) header area. This is faster than doing // two read operations in a row. Read as much as possible. If the file is cut, // the FileStream will pad it with zeros if (FileStream_Read(pFileSpan->pStream, &ReadOffset, pbEncodedBuffer, (DWORD)cbEncodedBuffer)) { // Parse the BLTE header dwErrCode = ParseBlteHeader(pFileSpan, pCKeyEntry, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, &cbHeaderSize); if (dwErrCode == ERROR_SUCCESS) { // If the headers are larger than the initial read size, we read the missing data pFileSpan->HeaderSize = (DWORD)(cbTotalHeaderSize = cbHeaderSize + (pFileSpan->FrameCount * sizeof(BLTE_FRAME))); if (cbTotalHeaderSize > cbEncodedBuffer) { pbEncodedBuffer = ReadMissingHeaderData(pFileSpan, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, cbTotalHeaderSize); if (pbEncodedBuffer == NULL) dwErrCode = GetCascError(); cbEncodedBuffer = cbTotalHeaderSize; } // Load the array of frame headers if (dwErrCode == ERROR_SUCCESS) { assert((ReadOffset + cbHeaderSize) > ReadOffset); dwErrCode = LoadSpanFrames(pFileSpan, pCKeyEntry, ReadOffset + cbHeaderSize, pbEncodedBuffer + cbHeaderSize, pbEncodedBuffer + cbEncodedBuffer, cbHeaderSize); } } else { // Special treatment for plain files ("PATCH"): If the content size and encoded size // are equal, we will create a single fake frame if(pCKeyEntry->EncodedSize == pCKeyEntry->ContentSize) { dwErrCode = LoadSpanFramesForPlainFile(pFileSpan, pCKeyEntry); } } } else { dwErrCode = ERROR_FILE_CORRUPT; } // Free the frame buffer CASC_FREE(pbEncodedBuffer); } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } return dwErrCode; } static DWORD LoadSpanFrames(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry) { DWORD dwErrCode = ERROR_SUCCESS; // Sanity check assert(pFileSpan->pFrames == NULL); // Make sure that the data stream is open for that span if(pFileSpan->pStream == NULL) { dwErrCode = OpenDataStream(hf, pFileSpan, pCKeyEntry, hf->bDownloadFileIf); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; } // Make sure we have header area loaded return LoadEncodedHeaderAndSpanFrames(pFileSpan, pCKeyEntry); } // Loads all file spans to memory static DWORD LoadFileSpanFrames(TCascFile * hf) { PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry; PCASC_FILE_SPAN pFileSpan = hf->pFileSpan; DWORD dwErrCode = ERROR_SUCCESS; // If the ContentSize/EncodedSize is still unknown, we need to get it from the file frames if(hf->ContentSize == CASC_INVALID_SIZE64 || hf->EncodedSize == CASC_INVALID_SIZE64) { // Set initially to zero hf->ContentSize = 0; hf->EncodedSize = 0; // Load file frames for all spans for(DWORD i = 0; i < hf->SpanCount; i++, pCKeyEntry++, pFileSpan++) { // Init the range of the file span pFileSpan->StartOffset = hf->ContentSize; pFileSpan->EndOffset = hf->ContentSize; // Load the frames of the file span dwErrCode = LoadSpanFrames(hf, pFileSpan, pCKeyEntry); if(dwErrCode != ERROR_SUCCESS) break; hf->ContentSize += pCKeyEntry->ContentSize; hf->EncodedSize += pCKeyEntry->EncodedSize; pFileSpan->EndOffset = hf->ContentSize; } } else { // Load file frames for all spans for(DWORD i = 0; i < hf->SpanCount; i++, pCKeyEntry++, pFileSpan++) { // Load the frames of the file span dwErrCode = LoadSpanFrames(hf, pFileSpan, pCKeyEntry); if(dwErrCode != ERROR_SUCCESS) break; } } return dwErrCode; } static DWORD EnsureFileSpanFramesLoaded(TCascFile * hf) { DWORD dwErrCode; if(hf->ContentSize == CASC_INVALID_SIZE64 || hf->pFileSpan->pFrames == NULL) { // Load all frames of all file spans dwErrCode = LoadFileSpanFrames(hf); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Now the content size must be known if(hf->ContentSize == CASC_INVALID_SIZE64) return ERROR_CAN_NOT_COMPLETE; } return ERROR_SUCCESS; } static DWORD DecodeFileFrame( TCascFile * hf, PCASC_CKEY_ENTRY pCKeyEntry, PCASC_FILE_FRAME pFrame, LPBYTE pbEncoded, LPBYTE pbDecoded, DWORD FrameIndex) { TCascStorage * hs = hf->hs; LPBYTE pbWorkBuffer = NULL; DWORD cbDecodedExpected = 0; DWORD cbWorkBuffer = 0; DWORD dwStepCount = 0; DWORD dwErrCode = ERROR_SUCCESS; DWORD cbEncoded = pFrame->EncodedSize; DWORD cbDecoded = pFrame->ContentSize; bool bWorkComplete = false; //if(pFrame->EncodedSize == 0xda001) //{ // FILE * fp = fopen("E:\\frame-da001-002.dat", "wb"); // fwrite(pbEncoded, 1, pFrame->EncodedSize, fp); // fclose(fp); //} // If this is a file span with plain data, just copy the data if(pCKeyEntry->Flags & CASC_CE_PLAIN_DATA) { assert(pCKeyEntry->ContentSize == pCKeyEntry->EncodedSize); assert(pCKeyEntry->ContentSize == pFrame->ContentSize); assert(pFrame->ContentSize == pFrame->EncodedSize); memcpy(pbDecoded, pbEncoded, pCKeyEntry->ContentSize); return ERROR_SUCCESS; } // Shall we verify the frame integrity? if(hf->bVerifyIntegrity) { if(!CascVerifyDataBlockHash(pbEncoded, pFrame->EncodedSize, pFrame->FrameHash.Value)) return ERROR_FILE_CORRUPT; } // Perform the loop while(bWorkComplete == false) { // There should never be a 3rd step assert(dwStepCount < 2); // Perform the operation specific by the first byte switch(pbEncoded[0]) { case 'E': // Encrypted files // The work buffer should not have been allocated by any step assert(pbWorkBuffer == NULL && cbWorkBuffer == 0); // Allocate temporary buffer to decrypt into // Example storage: "2016 - WoW/23420", File: "4ee6bc9c6564227f1748abd0b088e950" pbWorkBuffer = CASC_ALLOC(cbEncoded - 1); cbWorkBuffer = cbEncoded - 1; if(pbWorkBuffer == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Decrypt the stream to the work buffer dwErrCode = CascDecrypt(hs, pbWorkBuffer, &cbWorkBuffer, pbEncoded + 1, cbEncoded - 1, FrameIndex); if(dwErrCode != ERROR_SUCCESS) { bWorkComplete = true; break; } // When encrypted, there is always one more step after this. // Setup the work buffer as input buffer for the next operation pbEncoded = pbWorkBuffer; cbEncoded = cbWorkBuffer; break; case 'Z': // ZLIB compressed files // If we decompressed less than expected, we simply fill the rest with zeros // Example: INSTALL file from the TACT CASC storage cbDecodedExpected = cbDecoded; dwErrCode = CascDecompress(pbDecoded, &cbDecoded, pbEncoded + 1, cbEncoded - 1); // We exactly know what the output buffer size will be. // If the uncompressed data is smaller, fill the rest with zeros if(cbDecoded < cbDecodedExpected) memset(pbDecoded + cbDecoded, 0, (cbDecodedExpected - cbDecoded)); bWorkComplete = true; break; case 'N': // Normal stored files dwErrCode = CascDirectCopy(pbDecoded, &cbDecoded, pbEncoded + 1, cbEncoded - 1); bWorkComplete = true; break; case 'F': // Recursive frames (not supported) default: // Unrecognized. Could be a plain file data dwErrCode = ERROR_NOT_SUPPORTED; bWorkComplete = true; assert(false); break; } // Increment the step count dwStepCount++; } // Some people find it handy to extract data from partially encrypted file, // even at the cost of producing corrupt files. // We overcome missing decryption key by zeroing the encrypted portions if(dwErrCode == ERROR_FILE_ENCRYPTED && hf->bOvercomeEncrypted) { memset(pbDecoded, 0, cbDecoded); dwErrCode = ERROR_SUCCESS; } // Free the temporary buffer CASC_FREE(pbWorkBuffer); return dwErrCode; } static bool GetFileFullInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded) { PCASC_FILE_FULL_INFO pFileInfo; PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry; TCascStorage * hs = hf->hs; DWORD dwErrCode; // Make sure that the file spans are loaded dwErrCode = EnsureFileSpanFramesLoaded(hf); if(dwErrCode != ERROR_SUCCESS) { SetCascError(dwErrCode); return false; } // Verify whether we have enough space in the buffer pFileInfo = (PCASC_FILE_FULL_INFO)ProbeOutputBuffer(pvFileInfo, cbFileInfo, sizeof(CASC_FILE_FULL_INFO), pcbLengthNeeded); if(pFileInfo != NULL) { // Reset the entire structure CopyMemory16(pFileInfo->CKey, pCKeyEntry->CKey); CopyMemory16(pFileInfo->EKey, pCKeyEntry->EKey); pFileInfo->FileDataId = CASC_INVALID_ID; pFileInfo->LocaleFlags = CASC_INVALID_ID; pFileInfo->ContentFlags = CASC_INVALID_ID; // Supply information not depending on root CascStrPrintf(pFileInfo->DataFileName, _countof(pFileInfo->DataFileName), "data.%03u", hf->pFileSpan->ArchiveIndex); pFileInfo->StorageOffset = pCKeyEntry->StorageOffset; pFileInfo->SegmentOffset = hf->pFileSpan->ArchiveOffs; pFileInfo->FileNameHash = 0; pFileInfo->TagBitMask = pCKeyEntry->TagBitMask; pFileInfo->ContentSize = hf->ContentSize; pFileInfo->EncodedSize = hf->EncodedSize; pFileInfo->SegmentIndex = hf->pFileSpan->ArchiveIndex; pFileInfo->SpanCount = hf->SpanCount; // Supply the root-specific information hs->pRootHandler->GetInfo(pCKeyEntry, pFileInfo); } return (pFileInfo != NULL); } static bool GetFileSpanInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded) { PCASC_FILE_SPAN_INFO pFileInfo; PCASC_FILE_SPAN pFileSpan = hf->pFileSpan; PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry; DWORD dwErrCode = ERROR_SUCCESS; // Make sure that the file spans are loaded dwErrCode = EnsureFileSpanFramesLoaded(hf); if(dwErrCode != ERROR_SUCCESS) { SetCascError(dwErrCode); return false; } // Verify whether we have enough space in the buffer pFileInfo = (PCASC_FILE_SPAN_INFO)ProbeOutputBuffer(pvFileInfo, cbFileInfo, sizeof(CASC_FILE_SPAN_INFO) * hf->SpanCount, pcbLengthNeeded); if(pFileInfo != NULL) { // Copy all file spans for(DWORD i = 0; i < hf->SpanCount; i++, pFileInfo++, pFileSpan++, pCKeyEntry++) { CopyMemory16(pFileInfo->CKey, pCKeyEntry->CKey); CopyMemory16(pFileInfo->EKey, pCKeyEntry->EKey); pFileInfo->StartOffset = pFileSpan->StartOffset; pFileInfo->EndOffset = pFileSpan->EndOffset; pFileInfo->ArchiveIndex = pFileSpan->ArchiveIndex; pFileInfo->ArchiveOffs = pFileSpan->ArchiveOffs; pFileInfo->HeaderSize = pFileSpan->HeaderSize; pFileInfo->FrameCount = pFileSpan->FrameCount; } } return (pFileInfo != NULL); } // Reads the file data from cache. Returns the number of bytes read static DWORD ReadFile_Cache(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset) { // Is there a file cache at all? if(hf->pbFileCache != NULL && hf->FileCacheStart <= StartOffset && StartOffset < hf->FileCacheEnd) { LPBYTE pbStartBlock = hf->pbFileCache + (size_t)(StartOffset - hf->FileCacheStart); // Can we handle the entire request from the cache? if(EndOffset <= hf->FileCacheEnd) { DWORD dwBytesToCopy = (DWORD)(EndOffset - StartOffset); memcpy(pbBuffer, pbStartBlock, dwBytesToCopy); return dwBytesToCopy; } // We copy as much bytes as available. The rest is handled by normal read else { DWORD dwBytesToCopy = (DWORD)(hf->FileCacheEnd - StartOffset); memcpy(pbBuffer, pbStartBlock, dwBytesToCopy); return dwBytesToCopy; } } // Can't handle the request from the cache return 0; } // No cache at all. The entire file will be read directly to the user buffer static DWORD ReadFile_WholeFile(TCascFile * hf, LPBYTE pbBuffer) { PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry; PCASC_FILE_SPAN pFileSpan = hf->pFileSpan; LPBYTE pbSaveBuffer = pbBuffer; LPBYTE pbEncoded; LPBYTE pbEncodedPtr; DWORD dwErrCode; for(DWORD SpanIndex = 0; SpanIndex < hf->SpanCount; SpanIndex++, pCKeyEntry++, pFileSpan++) { ULONGLONG ByteOffset = pFileSpan->ArchiveOffs + pFileSpan->HeaderSize; DWORD EncodedSize = pCKeyEntry->EncodedSize - pFileSpan->HeaderSize; // Allocate the buffer for the entire encoded span pbEncodedPtr = pbEncoded = CASC_ALLOC(EncodedSize); if(pbEncoded == NULL) { SetCascError(ERROR_NOT_ENOUGH_MEMORY); return 0; } // Load the encoded buffer if(FileStream_Read(pFileSpan->pStream, &ByteOffset, pbEncoded, EncodedSize)) { PCASC_FILE_FRAME pFileFrame = pFileSpan->pFrames; for(DWORD FrameIndex = 0; FrameIndex < pFileSpan->FrameCount; FrameIndex++, pFileFrame++) { // Decode the file frame dwErrCode = DecodeFileFrame(hf, pCKeyEntry, pFileFrame, pbEncodedPtr, pbBuffer, FrameIndex); if(dwErrCode != ERROR_SUCCESS) break; // Move pointers pbEncodedPtr += pFileFrame->EncodedSize; pbBuffer += pFileFrame->ContentSize; } } CASC_FREE(pbEncoded); } // Give the amount of bytes read return (DWORD)(pbBuffer - pbSaveBuffer); } static DWORD ReadFile_FrameCached(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset) { PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry; PCASC_FILE_SPAN pFileSpan = hf->pFileSpan; PCASC_FILE_FRAME pFileFrame = NULL; LPBYTE pbSaveBuffer = pbBuffer; LPBYTE pbEncoded = NULL; LPBYTE pbDecoded = NULL; DWORD dwBytesRead = 0; DWORD dwErrCode = ERROR_SUCCESS; bool bNeedFreeDecoded = true; // Parse all file spans for(DWORD SpanIndex = 0; SpanIndex < hf->SpanCount; SpanIndex++, pCKeyEntry++, pFileSpan++) { if(pFileSpan->StartOffset <= StartOffset && StartOffset < pFileSpan->EndOffset) { for(DWORD FrameIndex = 0; FrameIndex < pFileSpan->FrameCount; FrameIndex++) { // Get the current file frame pFileFrame = pFileSpan->pFrames + FrameIndex; // Check the frame byte range if(pFileFrame->StartOffset <= StartOffset && StartOffset < pFileFrame->EndOffset) { // Check bytes read overflow if((dwBytesRead + pFileFrame->ContentSize) < dwBytesRead) { SetCascError(ERROR_BUFFER_OVERFLOW); return 0; } // Pick the buffer for decoded data. If we are going to read the entire frame, // there is a little chance that the caller will read the same file range again // So we can as well just unpack the entire frame into the output buffer if(pFileFrame->StartOffset < StartOffset || EndOffset < pFileFrame->EndOffset) { if((pbDecoded = CASC_ALLOC(pFileFrame->ContentSize)) == NULL) { SetCascError(ERROR_NOT_ENOUGH_MEMORY); return 0; } bNeedFreeDecoded = true; } else { bNeedFreeDecoded = false; pbDecoded = pbBuffer; } // Allocate the encoded frame if((pbEncoded = CASC_ALLOC(pFileFrame->EncodedSize)) == NULL) { CASC_FREE(pbDecoded); SetCascError(ERROR_NOT_ENOUGH_MEMORY); return 0; } // Load the frame to the encoded buffer if(FileStream_Read(pFileSpan->pStream, &pFileFrame->DataFileOffset, pbEncoded, pFileFrame->EncodedSize)) { ULONGLONG EndOfCopy = CASCLIB_MIN(pFileFrame->EndOffset, EndOffset); DWORD dwBytesToCopy = (DWORD)(EndOfCopy - StartOffset); // Decode the frame dwErrCode = DecodeFileFrame(hf, pCKeyEntry, pFileFrame, pbEncoded, pbDecoded, FrameIndex); if(dwErrCode == ERROR_SUCCESS) { // Copy the data if(pbDecoded != pbBuffer) memcpy(pbBuffer, pbDecoded + (DWORD)(StartOffset - pFileFrame->StartOffset), dwBytesToCopy); StartOffset += dwBytesToCopy; pbBuffer += dwBytesToCopy; } } // Free the encoded buffer CASC_FREE(pbEncoded); // If we are at the end of the read area, break all loops if(dwErrCode != ERROR_SUCCESS || StartOffset >= EndOffset) goto __WorkComplete; if(bNeedFreeDecoded) CASC_FREE(pbDecoded); } } } } __WorkComplete: if(dwErrCode == ERROR_SUCCESS) { // If there is some data left in the frame, we set it as cache if(pFileFrame != NULL && pbDecoded != NULL && EndOffset < pFileFrame->EndOffset) { CASC_FREE(hf->pbFileCache); hf->FileCacheStart = pFileFrame->StartOffset; hf->FileCacheEnd = pFileFrame->EndOffset; hf->pbFileCache = pbDecoded; pbDecoded = NULL; } } // Final free of the decoded buffer, if needeed if(bNeedFreeDecoded) CASC_FREE(pbDecoded); pbDecoded = NULL; // Return the number of bytes read. Always set LastError. SetCascError(dwErrCode); return (DWORD)(pbBuffer - pbSaveBuffer); } // No cache at all. The entire file will be read directly to the user buffer static DWORD ReadFile_NonCached(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset) { // Reading the whole file? if(StartOffset == 0 && EndOffset == hf->ContentSize) { return ReadFile_WholeFile(hf, pbBuffer); } // Reading just a part of the file? else { assert(false); } return 0; } //----------------------------------------------------------------------------- // Public functions bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded) { TCascFile * hf; LPBYTE pbOutputValue = NULL; LPBYTE pbInfoValue = NULL; size_t cbInfoValue = 0; // Validate the file handle if((hf = TCascFile::IsValid(hFile)) == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // Differentiate between info classes switch(InfoClass) { case CascFileContentKey: // Do we have content key at all? if(hf->pCKeyEntry == NULL || (hf->pCKeyEntry->Flags & CASC_CE_HAS_CKEY) == 0) { SetCascError(ERROR_NOT_SUPPORTED); return false; } // Give the content key pbInfoValue = hf->pCKeyEntry->CKey; cbInfoValue = CASC_CKEY_SIZE; break; case CascFileEncodedKey: // Do we have content key at all? if(hf->pCKeyEntry == NULL || (hf->pCKeyEntry->Flags & CASC_CE_HAS_EKEY) == 0) { SetCascError(ERROR_NOT_SUPPORTED); return false; } // Give the content key pbInfoValue = hf->pCKeyEntry->EKey; cbInfoValue = CASC_CKEY_SIZE; break; case CascFileFullInfo: return GetFileFullInfo(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded); case CascFileSpanInfo: return GetFileSpanInfo(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded); default: SetCascError(ERROR_INVALID_PARAMETER); return false; } // Sanity check assert(pbInfoValue != NULL); assert(cbInfoValue != 0); // Give the result pbOutputValue = (LPBYTE)ProbeOutputBuffer(pvFileInfo, cbFileInfo, cbInfoValue, pcbLengthNeeded); if(pbOutputValue != NULL) memcpy(pbOutputValue, pbInfoValue, cbInfoValue); return (pbOutputValue != NULL); } // // THE FILE SIZE PROBLEM // // There are members called "FileSize" in many CASC-related structure // For various files, these variables have different meaning. // // Storage FileName FileSize FrameSum HdrArea CKeyEntry EKeyEntry RootEntry // ----------- -------- ---------- -------- -------- ---------- ---------- ---------- // HotS(29049) ENCODING 0x0024BA45 - 0x0024b98a 0x0024BA45 n/a 0x0024BA45 n/a // HotS(29049) ROOT 0x00193340 - 0x00193340 0x0010db65 0x00193340 0x0010db65 n/a // HotS(29049) (other) 0x00001080 - 0x00001080 0x000008eb 0x00001080 0x000008eb 0x00001080 // // WoW(18888) ENCODING 0x030d487b - 0x030dee79 0x030d487b n/a 0x030d487b n/a // WoW(18888) ROOT 0x016a9800 - n/a 0x0131313d 0x016a9800 0x0131313d n/a // WoW(18888) (other) 0x000007d0 - 0x000007d0 0x00000397 0x000007d0 0x00000397 n/a // bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize) { TCascFile * hf; DWORD dwErrCode; // Validate the file handle if((hf = TCascFile::IsValid(hFile)) == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // Validate the file pointer if(PtrFileSize == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // ENCODING on older storages: Content size is not present in the BUILD file // For that reason, we need to query the content size from the file frames dwErrCode = EnsureFileSpanFramesLoaded(hf); if(dwErrCode != ERROR_SUCCESS) { SetCascError(dwErrCode); return false; } // Give the file size to the caller PtrFileSize[0] = hf->ContentSize; return true; } DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD PtrFileSizeHigh) { ULONGLONG FileSize = 0; // Retrieve the 64-bit file size if(!CascGetFileSize64(hFile, &FileSize)) return CASC_INVALID_SIZE; // Give the file size to the caller if(PtrFileSizeHigh != NULL) PtrFileSizeHigh[0] = (DWORD)(FileSize >> 32); return (DWORD)(FileSize); } bool WINAPI CascSetFilePointer64(HANDLE hFile, LONGLONG DistanceToMove, PULONGLONG PtrNewPos, DWORD dwMoveMethod) { ULONGLONG FilePosition; TCascFile * hf; // If the hFile is not a valid file handle, return an error. hf = TCascFile::IsValid(hFile); if(hf == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // Get the relative point where to move from switch(dwMoveMethod) { case FILE_BEGIN: FilePosition = 0; break; case FILE_CURRENT: FilePosition = hf->FilePointer; break; case FILE_END: FilePosition = hf->ContentSize; break; default: SetCascError(ERROR_INVALID_PARAMETER); return false; } // Now calculate the new file pointer if(DistanceToMove >= 0) { // Do not allow the file pointer to overflow 64-bit range if((FilePosition + DistanceToMove) < FilePosition) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Do not allow the file pointer to overflow the file size if((FilePosition = FilePosition + DistanceToMove) > hf->ContentSize) FilePosition = hf->ContentSize; hf->FilePointer = FilePosition; } else { // Do not allow the file pointer to underflow 64-bit range if((FilePosition + DistanceToMove) > FilePosition) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Do not allow the file pointer to move to negative values if((LONGLONG)(FilePosition = FilePosition + DistanceToMove) < 0) FilePosition = 0; hf->FilePointer = FilePosition; } // Give the result size to the caller if(PtrNewPos != NULL) PtrNewPos[0] = hf->FilePointer; return true; } DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * PtrFilePosHigh, DWORD dwMoveMethod) { ULONGLONG NewPos = 0; LONGLONG DistanceToMove; // Assemble the 64-bit distance to move DistanceToMove = (PtrFilePosHigh != NULL) ? MAKE_OFFSET64(PtrFilePosHigh[0], lFilePos) : (LONGLONG)(LONG)lFilePos; // Set the file offset if(!CascSetFilePointer64(hFile, DistanceToMove, &NewPos, dwMoveMethod)) return CASC_INVALID_POS; // Give the result to the caller if(PtrFilePosHigh != NULL) PtrFilePosHigh[0] = (LONG)(NewPos >> 32); return (DWORD)(NewPos); } bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD PtrBytesRead) { ULONGLONG SaveFilePointer; ULONGLONG StartOffset; ULONGLONG EndOffset; TCascFile * hf; LPBYTE pbBuffer = (LPBYTE)pvBuffer; DWORD dwBytesRead1 = 0; // From cache DWORD dwBytesRead2 = 0; // From file DWORD dwErrCode; // The buffer must be valid if(pvBuffer == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Validate the file handle if((hf = TCascFile::IsValid(hFile)) == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // If we don't have file frames loaded, we need to do it now. // Need to do it before file range check, as the file size may be unknown at this point dwErrCode = EnsureFileSpanFramesLoaded(hf); if(dwErrCode != ERROR_SUCCESS) { SetCascError(dwErrCode); return false; } // If the file position is at or beyond end of file, do nothing SaveFilePointer = StartOffset = hf->FilePointer; if(StartOffset >= hf->ContentSize) { if (PtrBytesRead != NULL) PtrBytesRead[0] = 0; return true; } // If the read area goes beyond end of the file, cut the number of bytes to read EndOffset = StartOffset + dwBytesToRead; if(EndOffset > hf->ContentSize) { EndOffset = hf->ContentSize; } // Can we handle the request (at least partially) from the cache? if((dwBytesRead1 = ReadFile_Cache(hf, pbBuffer, StartOffset, EndOffset)) != 0) { // Move pointers StartOffset = StartOffset + dwBytesRead1; pbBuffer += dwBytesRead1; // Has the read request been fully satisfied? if(StartOffset == EndOffset) { if(PtrBytesRead != NULL) PtrBytesRead[0] = dwBytesRead1; hf->FilePointer = EndOffset; return true; } } // Perform the cache-strategy-specific read switch(hf->CacheStrategy) { // No caching at all. The entire file will be read directly to the user buffer // Used for loading internal files, where we need to read the whole file case CascCacheNothing: dwBytesRead2 = ReadFile_NonCached(hf, pbBuffer, StartOffset, EndOffset); break; // Read as many frames as we can. The last loaded frame, if not read entirely, // will stay in the cache - We expect the next read to continue from that offset. case CascCacheLastFrame: dwBytesRead2 = ReadFile_FrameCached(hf, pbBuffer, StartOffset, EndOffset); break; } // If the second-stage-read failed, we invalidate the entire operation and return 0 bytes read if(dwBytesRead2 != 0) { // Give the result to the caller if(PtrBytesRead != NULL) PtrBytesRead[0] = (dwBytesRead1 + dwBytesRead2); hf->FilePointer = StartOffset + dwBytesRead2; return true; } else { // Give the result to the caller if(PtrBytesRead != NULL) PtrBytesRead[0] = 0; hf->FilePointer = SaveFilePointer; // If 0 bytes were requested, it's actually a success return (dwBytesToRead == 0); } } ================================================ FILE: deps/CascLib/src/CascRootFile_Diablo3.cpp ================================================ /*****************************************************************************/ /* CascRootFile_Diablo3.cpp Copyright (c) Ladislav Zezula 2015 */ /*---------------------------------------------------------------------------*/ /* Support for loading Diablo 3 ROOT file */ /* Note: D3 offsets refer to Diablo III.exe 2.2.0.30013 (32-bit) */ /* SHA1: e4f17eca8aad8dde70870bf932ac3f5b85f17a1f */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 04.03.15 1.00 Lad The first version of CascRootFile_Diablo3.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Local structures #define DIABLO3_SUBDIR_SIGNATURE 0xEAF1FE87 #define DIABLO3_PACKAGES_SIGNATURE 0xAABB0002 #define DIABLO3_MAX_SUBDIRS 0x20 #define DIABLO3_MAX_ASSETS 70 // Maximum possible number of assets #define DIABLO3_MAX_ROOT_FOLDERS 0x20 // Maximum count of root directory named entries // On-disk structure for a file given by file number typedef struct _DIABLO3_ASSET_ENTRY { CONTENT_KEY CKey; // Content key for the file DWORD FileIndex; // File index } DIABLO3_ASSET_ENTRY, *PDIABLO3_ASSET_ENTRY; // On-disk structure for a file given by file number and suffix typedef struct _DIABLO3_ASSETIDX_ENTRY { CONTENT_KEY CKey; // Content key for the file DWORD FileIndex; // File index DWORD SubIndex; // File subindex, like "SoundBank\3D Ambience\0000.smp" } DIABLO3_ASSETIDX_ENTRY, *PDIABLO3_ASSETIDX_ENTRY; // In-memory structure of the named entry typedef struct _DIABLO3_NAMED_ENTRY { PCONTENT_KEY pCKey; // Pointer to the content key const char * szFileName; // Pointer to the zero-terminated file name const char * szFileEnd; // Position of the zero terminator (aka end of the file name) } DIABLO3_NAMED_ENTRY, *PDIABLO3_NAMED_ENTRY; // On-disk structure of CoreToc.dat header typedef struct _DIABLO3_CORE_TOC_HEADER { DWORD EntryCounts[DIABLO3_MAX_ASSETS]; // Array of number of entries (files) for each asset DWORD EntryOffsets[DIABLO3_MAX_ASSETS]; // Array of offsets of each DIABLO3_CORE_TOC_ENTRY, relative to data after header DWORD Unknowns[DIABLO3_MAX_ASSETS]; // Unknown DWORD Alignment; } DIABLO3_CORE_TOC_HEADER, *PDIABLO3_CORE_TOC_HEADER; // On-disk structure of the entry in CoreToc.dat typedef struct _DIABLO3_CORE_TOC_ENTRY { DWORD AssetIndex; // Index of the Diablo3 asset (aka directory) DWORD FileIndex; // File index DWORD NameOffset; // Offset of the plain file name } DIABLO3_CORE_TOC_ENTRY, *PDIABLO3_CORE_TOC_ENTRY; // In-memory structure of parsed directory data typedef struct _DIABLO3_DIRECTORY { LPBYTE pbDirectoryData; // The begin of the directory data block LPBYTE pbDirectoryEnd; // The end of the directory data block LPBYTE pbAssetEntries; // Pointer to asset entries without subitem number. Example: "SoundBank\SoundFile.smp" LPBYTE pbAssetIdxEntries; // Pointer to asset entries with subitem number LPBYTE pbNamedEntries; // Pointer to named entries. These are for files with arbitrary names, and they do not belong to an asset DWORD dwAssetEntries; // Number of asset entries without subitem number DWORD dwAssetIdxEntries; DWORD dwNamedEntries; DWORD dwNodeIndex; // Index of file node for this folder } DIABLO3_DIRECTORY, *PDIABLO3_DIRECTORY; // Structure for conversion DirectoryID -> Directory name typedef struct _DIABLO3_ASSET_INFO { const char * szDirectoryName; // Directory name const char * szExtension; } DIABLO3_ASSET_INFO; typedef const DIABLO3_ASSET_INFO * PDIABLO3_ASSET_INFO; //----------------------------------------------------------------------------- // Local variables static const DIABLO3_ASSET_INFO Assets[] = { // DIR-NAME EXTENSION // ========== ========= {NULL, NULL}, // 0x00 {"Actor", "acr"}, // 0x01 {"Adventure", "adv"}, // 0x02 {NULL, NULL}, // 0x03 {NULL, NULL}, // 0x04 {"AmbientSound", "ams"}, // 0x05 {"Anim", "ani"}, // 0x06 {"Anim2D", "an2"}, // 0x07 {"AnimSet", "ans"}, // 0x08 {"Appearance", "app"}, // 0x09 {NULL, NULL}, // 0x0A {"Cloth", "clt"}, // 0x0B {"Conversation", "cnv"}, // 0x0C {NULL, NULL}, // 0x0D {"EffectGroup", "efg"}, // 0x0E {"Encounter", "enc"}, // 0x0F {NULL, NULL}, // 0x10 {"Explosion", "xpl"}, // 0x11 {NULL, NULL}, // 0x12 {"Font", "fnt"}, // 0x13 {"GameBalance", "gam"}, // 0x14 {"Globals", "glo"}, // 0x15 {"LevelArea", "lvl"}, // 0x16 {"Light", "lit"}, // 0x17 {"MarkerSet", "mrk"}, // 0x18 {"Monster", "mon"}, // 0x19 {"Observer", "obs"}, // 0x1A {"Particle", "prt"}, // 0x1B {"Physics", "phy"}, // 0x1C {"Power", "pow"}, // 0x1D {NULL, NULL}, // 0x1E {"Quest", "qst"}, // 0x1F {"Rope", "rop"}, // 0x20 {"Scene", "scn"}, // 0x21 {"SceneGroup", "scg"}, // 0x22 {NULL, NULL}, // 0x23 {"ShaderMap", "shm"}, // 0x24 {"Shaders", "shd"}, // 0x25 {"Shakes", "shk"}, // 0x26 {"SkillKit", "skl"}, // 0x27 {"Sound", "snd"}, // 0x28 {"SoundBank", "sbk"}, // 0x29 {"StringList", "stl"}, // 0x2A {"Surface", "srf"}, // 0x2B {"Textures", "tex"}, // 0x2C {"Trail", "trl"}, // 0x2D {"UI", "ui"}, // 0x2E {"Weather", "wth"}, // 0x2F {"Worlds", "wrl"}, // 0x30 {"Recipe", "rcp"}, // 0x31 {NULL, NULL}, // 0x32 {"Condition", "cnd"}, // 0x33 {NULL, NULL}, // 0x34 {NULL, NULL}, // 0x35 {NULL, NULL}, // 0x36 {NULL, NULL}, // 0x37 {"Act", "act"}, // 0x38 {"Material", "mat"}, // 0x39 {"QuestRange", "qsr"}, // 0x3A {"Lore", "lor"}, // 0x3B {"Reverb", "rev"}, // 0x3C {"PhysMesh", "phm"}, // 0x3D {"Music", "mus"}, // 0x3E {"Tutorial", "tut"}, // 0x3F {"BossEncounter", "bos"}, // 0x40 {NULL, NULL}, // 0x41 {"Accolade", "aco"}, // 0x42 }; #define DIABLO3_ASSET_COUNT (sizeof(Assets) / sizeof(Assets[0])) //----------------------------------------------------------------------------- // Handler definitions for Diablo3 root file struct TDiabloRoot : public TFileTreeRoot { public: TDiabloRoot() : TFileTreeRoot(0) { memset(RootFolders, 0, sizeof(RootFolders)); pFileIndices = NULL; pbCoreTocFile = NULL; pbCoreTocData = NULL; nFileIndices = 0; cbCoreTocFile = 0; // Map for searching a real file extension memset(&PackagesMap, 0, sizeof(CASC_MAP)); pbPackagesDat = NULL; cbPackagesDat = 0; // We have file names and return CKey as result of search dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY); } ~TDiabloRoot() { FreeLoadingStuff(); } PDIABLO3_ASSET_INFO GetAssetInfo(DWORD dwAssetIndex) { if(dwAssetIndex < DIABLO3_ASSET_COUNT && Assets[dwAssetIndex].szDirectoryName != NULL) return &Assets[dwAssetIndex]; return NULL; } char * FindPackageName(const char * szAssetName, const char * szPlainName) { char szFileName[MAX_PATH+1]; size_t nLength; // Construct the name without extension and find it in the map nLength = CascStrPrintf(szFileName, _countof(szFileName), "%s\\%s", szAssetName, szPlainName); return (char *)PackagesMap.FindString(szFileName, szFileName + nLength); } LPBYTE LoadFileToMemory(TCascStorage * hs, const char * szFileName, DWORD * pcbFileData) { PCASC_CKEY_ENTRY pCKeyEntry; LPBYTE pbFileData = NULL; // Try to find CKey for the file pCKeyEntry = GetFile(hs, szFileName); if(pCKeyEntry != NULL) pbFileData = LoadInternalFileToMemory(hs, pCKeyEntry, pcbFileData); return pbFileData; } static LPBYTE CaptureDirectoryData( DIABLO3_DIRECTORY & DirHeader, LPBYTE pbDirectory, DWORD cbDirectory) { LPBYTE pbDirectoryData = pbDirectory; LPBYTE pbDataEnd = pbDirectory + cbDirectory; DWORD Signature = 0; // // Structure of a Diablo3 directory header // 1) Signature (4 bytes) // 2) Number of DIABLO3_ASSET_ENTRY entries (4 bytes) // 3) Array of DIABLO3_ASSET_ENTRY entries // 4) Number of DIABLO3_ASSETIDX_ENTRY entries (4 bytes) // 5) Array of DIABLO3_ASSETIDX_ENTRY entries // 6) Number of DIABLO3_NAMED_ENTRY entries (4 bytes) // 7) Array of DIABLO3_NAMED_ENTRY entries // // Prepare the header signature memset(&DirHeader, 0, sizeof(DIABLO3_DIRECTORY)); // Get the header signature pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &Signature); if((pbDirectory == NULL) || (Signature != CASC_DIABLO3_ROOT_SIGNATURE && Signature != DIABLO3_SUBDIR_SIGNATURE)) return NULL; // Subdirectories have extra two arrays if(Signature == DIABLO3_SUBDIR_SIGNATURE) { // Capture the number of DIABLO3_ASSET_ENTRY items pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwAssetEntries); if(pbDirectory == NULL) return NULL; // Capture the array of DIABLO3_ASSET_ENTRY pbDirectory = CaptureArray(pbDirectory, pbDataEnd, &DirHeader.pbAssetEntries, DIABLO3_ASSET_ENTRY, DirHeader.dwAssetEntries); if(pbDirectory == NULL) return NULL; // Capture the number of DIABLO3_ASSETIDX_ENTRY items pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwAssetIdxEntries); if(pbDirectory == NULL) return NULL; // Capture the array of DIABLO3_ASSETIDX_ENTRY pbDirectory = CaptureArray(pbDirectory, pbDataEnd, &DirHeader.pbAssetIdxEntries, DIABLO3_ASSETIDX_ENTRY, DirHeader.dwAssetIdxEntries); if(pbDirectory == NULL) return NULL; } // Capture the number of DIABLO3_NAMED_ENTRY array pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwNamedEntries); if(pbDirectory == NULL) return NULL; // Note: Do not capture the array here. We will do that later, // when we will be parsing the directory DirHeader.pbNamedEntries = pbDirectory; // Put the directory range DirHeader.pbDirectoryData = pbDirectoryData; DirHeader.pbDirectoryEnd = pbDirectoryData + cbDirectory; return pbDirectory; } LPBYTE CaptureCoreTocHeader( PDIABLO3_CORE_TOC_HEADER * PtrHeader, PDWORD PtrMaxIndex, LPBYTE pbDataPtr, LPBYTE pbDataEnd) { PDIABLO3_CORE_TOC_HEADER pTocHeader = (PDIABLO3_CORE_TOC_HEADER)pbDataPtr; DWORD dwMaxFileIndex = 0; // Check the space for header if((pbDataPtr + sizeof(DIABLO3_CORE_TOC_HEADER)) > pbDataEnd) return NULL; pbDataPtr += sizeof(DIABLO3_CORE_TOC_HEADER); // Verify all asset arrays for(size_t i = 0; i < DIABLO3_MAX_ASSETS; i++) { PDIABLO3_CORE_TOC_ENTRY pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbDataPtr + pTocHeader->EntryOffsets[i]); DWORD EntryOffset = pTocHeader->EntryOffsets[i]; DWORD EntryCount = pTocHeader->EntryCounts[i]; // Verify file range if((pbDataPtr + EntryOffset + EntryCount * sizeof(DIABLO3_CORE_TOC_ENTRY)) > pbDataEnd) return NULL; // Find out the entry with the maximum index for(DWORD n = 0; n < EntryCount; n++) { if(pTocEntry->FileIndex >= dwMaxFileIndex) dwMaxFileIndex = pTocEntry->FileIndex; pTocEntry++; } } // Give data and return PtrMaxIndex[0] = dwMaxFileIndex; PtrHeader[0] = pTocHeader; return pbDataPtr; } LPBYTE CaptureNamedEntry( LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDIABLO3_NAMED_ENTRY pEntry) { // Capture the content key pbDataPtr = CaptureContentKey(pbDataPtr, pbDataEnd, &pEntry->pCKey); if(pbDataPtr == NULL) return NULL; // Capture file name. Must be ASCIIZ file name pEntry->szFileName = (const char *)pbDataPtr; while(pbDataPtr < pbDataEnd && pbDataPtr[0] != 0) pbDataPtr++; // Did we find a zero char? if(pbDataPtr < pbDataEnd && pbDataPtr[0] == 0) { pEntry->szFileEnd = (const char *)pbDataPtr; return pbDataPtr + 1; } return NULL; } int LoadDirectoryFile(TCascStorage * hs, DIABLO3_DIRECTORY & DirHeader, PCASC_CKEY_ENTRY pCKeyEntry) { LPBYTE pbData; DWORD cbData = 0; // Load the n-th folder pbData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbData); if(pbData && cbData) { if(CaptureDirectoryData(DirHeader, pbData, cbData) == NULL) { // Clear the directory CASC_FREE(pbData); return ERROR_BAD_FORMAT; } } return ERROR_SUCCESS; } bool CreateAssetFileName( CASC_PATH & PathBuffer, DWORD FileIndex, DWORD SubIndex) { PDIABLO3_CORE_TOC_ENTRY pTocEntry; PDIABLO3_ASSET_INFO pAssetInfo; LPCSTR szPackageName = NULL; LPCSTR szPlainName; LPCSTR szFormat; char szBuffer[MAX_PATH]; // Find and check the entry pTocEntry = pFileIndices + FileIndex; if(pTocEntry->FileIndex == FileIndex) { // Retrieve the asset information szPlainName = (LPCSTR)(pbCoreTocData + pTocEntry->NameOffset); pAssetInfo = GetAssetInfo(pTocEntry->AssetIndex); // Construct the file name, up to the extension. Don't include the '.' szFormat = (SubIndex != CASC_INVALID_INDEX) ? "%s\\%04u" : "%s"; CascStrPrintf(szBuffer, _countof(szBuffer), szFormat, szPlainName, SubIndex); // Try to fixup the file extension from the package name. // File extensions are not predictable because for subitems, // they are not always equal to the main items: // // SoundBank\3D Ambience.sbk // SoundBank\3D Ambience\0000.smp // SoundBank\3D Ambience\0002.smp // ... // SoundBank\Angel.sbk // SoundBank\Angel\0000.fsb // SoundBank\Angel\0002.fsb // // We use the Base\Data_D3\PC\Misc\Packages.dat for real file extensions, where possible // if(pAssetInfo != NULL) { // Retrieve the asset name szPackageName = FindPackageName(pAssetInfo->szDirectoryName, szBuffer); if(szPackageName != NULL) { PathBuffer.AppendString(szPackageName, false); return true; } // Append the directory name PathBuffer.AppendString(pAssetInfo->szDirectoryName, false); } else { // Append generic name "Asset##" and continue PathBuffer.AppendString("Asset", false); PathBuffer.AppendChar((char)('0' + (pTocEntry->AssetIndex / 10))); PathBuffer.AppendChar((char)('0' + (pTocEntry->AssetIndex % 10))); } // Append the content of the buffer PathBuffer.AppendString(szBuffer, true); // If we have an extension, use it. Otherwise, supply "a##" if(pAssetInfo != NULL && pAssetInfo->szExtension != NULL) { PathBuffer.AppendChar('.'); PathBuffer.AppendString(pAssetInfo->szExtension, false); } else { CascStrPrintf(szBuffer, _countof(szBuffer), ".a%02u", pTocEntry->AssetIndex); PathBuffer.AppendString(szBuffer, false); } return true; } return false; } // Parse the asset entries DWORD ParseAssetEntries( TCascStorage * hs, DIABLO3_DIRECTORY & Directory, CASC_PATH & PathBuffer) { PDIABLO3_ASSET_ENTRY pEntry = (PDIABLO3_ASSET_ENTRY)Directory.pbAssetEntries; PCASC_CKEY_ENTRY pCKeyEntry; size_t nSavePos = PathBuffer.Save(); DWORD dwEntries = Directory.dwAssetEntries; // Do nothing if there is no entries if(pEntry != NULL && dwEntries != 0) { // Insert all asset entries to the file tree for(DWORD i = 0; i < dwEntries; i++, pEntry++) { pCKeyEntry = FindCKeyEntry_CKey(hs, pEntry->CKey.Value); if(pCKeyEntry != NULL) { // Construct the full path name of the entry if(CreateAssetFileName(PathBuffer, pEntry->FileIndex, CASC_INVALID_INDEX)) { // Insert the entry to the file tree FileTree.InsertByName(pCKeyEntry, PathBuffer); } // Restore the path buffer position PathBuffer.Restore(nSavePos); } } } return ERROR_SUCCESS; } DWORD ParseAssetAndIdxEntries( TCascStorage * hs, DIABLO3_DIRECTORY & Directory, CASC_PATH & PathBuffer) { PDIABLO3_ASSETIDX_ENTRY pEntry = (PDIABLO3_ASSETIDX_ENTRY)Directory.pbAssetIdxEntries; PCASC_CKEY_ENTRY pCKeyEntry; size_t nSavePos = PathBuffer.Save(); DWORD dwEntries = Directory.dwAssetIdxEntries; // Do nothing if there is no entries if(pEntry != NULL && dwEntries != 0) { // Insert all asset entries to the file tree for(DWORD i = 0; i < dwEntries; i++, pEntry++) { pCKeyEntry = FindCKeyEntry_CKey(hs, pEntry->CKey.Value); if(pCKeyEntry != NULL) { // Construct the full path name of the entry if(CreateAssetFileName(PathBuffer, pEntry->FileIndex, pEntry->SubIndex)) { // Insert the entry to the file tree // fprintf(fp, "%08u %04u %s\n", pEntry->FileIndex, pEntry->SubIndex, PathBuffer.szBegin); FileTree.InsertByName(pCKeyEntry, PathBuffer); } // Restore the path buffer position PathBuffer.Restore(nSavePos); } } } return ERROR_SUCCESS; } // Parse the named entries of all folders DWORD ParseDirectory_Phase1( TCascStorage * hs, DIABLO3_DIRECTORY & Directory, CASC_PATH & PathBuffer, bool bIsRootDirectory) { DIABLO3_NAMED_ENTRY NamedEntry; size_t nFolderIndex = 0; size_t nSavePos = PathBuffer.Save(); DWORD dwErrCode = ERROR_SUCCESS; // Do nothing if there is no named headers if(Directory.pbNamedEntries && Directory.dwNamedEntries) { PCASC_CKEY_ENTRY pCKeyEntry; PCASC_FILE_NODE pFileNode; LPBYTE pbDataPtr = Directory.pbNamedEntries; LPBYTE pbDataEnd = Directory.pbDirectoryEnd; DWORD dwNodeIndex; // Parse all entries while(pbDataPtr < pbDataEnd) { // Capture the named entry pbDataPtr = CaptureNamedEntry(pbDataPtr, pbDataEnd, &NamedEntry); if(pbDataPtr == NULL) return ERROR_BAD_FORMAT; // Append the path fragment to the total path PathBuffer.AppendStringN(NamedEntry.szFileName, (NamedEntry.szFileEnd - NamedEntry.szFileName), true); // Check whether the file exists in the storage pCKeyEntry = FindCKeyEntry_CKey(hs, NamedEntry.pCKey->Value); if(pCKeyEntry != NULL) { // Create file node belonging to this folder pFileNode = FileTree.InsertByName(pCKeyEntry, PathBuffer); dwNodeIndex = (DWORD)FileTree.IndexOf(pFileNode); // If we are parsing root folder, we also need to load the data of the sub-folder file if(bIsRootDirectory) { // Mark the node as directory pCKeyEntry->Flags |= CASC_CE_FOLDER_ENTRY; pFileNode->Flags |= CFN_FLAG_FOLDER; // Load the sub-directory file dwErrCode = LoadDirectoryFile(hs, RootFolders[nFolderIndex], pCKeyEntry); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Parse the sub-directory file dwErrCode = ParseDirectory_Phase1(hs, RootFolders[nFolderIndex], PathBuffer, false); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Also save the item pointer and increment the folder index RootFolders[nFolderIndex].dwNodeIndex = dwNodeIndex; nFolderIndex++; } // Restore the path pointer PathBuffer.Restore(nSavePos); } } } return dwErrCode; } // Parse the nameless entries of all folders int ParseDirectory_Phase2(TCascStorage * hs) { CASC_PATH PathBuffer; char szBuffer[MAX_PATH]; // Parse each root subdirectory for(size_t i = 0; i < DIABLO3_MAX_ROOT_FOLDERS; i++) { // Is this root folder loaded? if(RootFolders[i].pbDirectoryData != NULL) { // Retrieve the parent name if(RootFolders[i].dwNodeIndex != 0) { FileTree.PathAt(szBuffer, _countof(szBuffer), RootFolders[i].dwNodeIndex); PathBuffer.SetPathRoot(szBuffer); } // Array of DIABLO3_ASSET_ENTRY entries. // These are for files belonging to an asset, without subitem number. // Example: "SoundBank\SoundFile.smp" ParseAssetEntries(hs, RootFolders[i], PathBuffer); // Array of DIABLO3_ASSETIDX_ENTRY entries. // These are for files belonging to an asset, with a subitem number. // Example: "SoundBank\SoundFile\0001.smp" ParseAssetAndIdxEntries(hs, RootFolders[i], PathBuffer); } } return ERROR_SUCCESS; } // Creates an array of DIABLO3_CORE_TOC_ENTRY entries indexed by FileIndex // Used as lookup table when we have FileIndex and need Asset+PlainName DWORD CreateMapOfFileIndices(TCascStorage * hs, const char * szFileName) { PDIABLO3_CORE_TOC_HEADER pTocHeader = NULL; LPBYTE pbCoreTocPtr = pbCoreTocFile; DWORD dwMaxFileIndex = 0; DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; // Load the entire file to memory pbCoreTocFile = pbCoreTocPtr = LoadFileToMemory(hs, szFileName, &cbCoreTocFile); if(pbCoreTocFile && cbCoreTocFile) { LPBYTE pbCoreTocEnd = pbCoreTocFile + cbCoreTocFile; // Capture the header if((pbCoreTocPtr = CaptureCoreTocHeader(&pTocHeader, &dwMaxFileIndex, pbCoreTocPtr, pbCoreTocEnd)) == NULL) return ERROR_BAD_FORMAT; // If there are no indices, return NULL if(dwMaxFileIndex == 0) return ERROR_SUCCESS; // Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs pFileIndices = CASC_ALLOC(dwMaxFileIndex + 1); if(pFileIndices != NULL) { // Initialize all entries to invalid memset(pFileIndices, 0xFF, (dwMaxFileIndex + 1) * sizeof(DIABLO3_CORE_TOC_ENTRY)); // Populate the linear array with the file indices for(size_t i = 0; i < DIABLO3_MAX_ASSETS; i++) { PDIABLO3_CORE_TOC_ENTRY pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocPtr + pTocHeader->EntryOffsets[i]); LPBYTE pbCoreTocNames = (LPBYTE)(pTocEntry + pTocHeader->EntryCounts[i]); // Setup the entries for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++) { DWORD dwFileIndex = pTocEntry->FileIndex; pFileIndices[dwFileIndex].AssetIndex = pTocEntry->AssetIndex; pFileIndices[dwFileIndex].FileIndex = pTocEntry->FileIndex; pFileIndices[dwFileIndex].NameOffset = (DWORD)(pbCoreTocNames - pbCoreTocPtr) + pTocEntry->NameOffset; pTocEntry++; } } // Save the file to the root handler pbCoreTocData = pbCoreTocPtr; nFileIndices = dwMaxFileIndex; dwErrCode = ERROR_SUCCESS; } } return dwErrCode; } // Packages.dat contains a list of full file names (without locale prefix). // They are not sorted, nor they correspond to file IDs. // Does the sort order mean something? Perhaps we could use them as listfile? int CreateMapOfRealNames(TCascStorage * hs, const char * szFileName) { DWORD Signature = 0; DWORD NumberOfNames = 0; // Load the entire file to memory pbPackagesDat = LoadFileToMemory(hs, szFileName, &cbPackagesDat); if(pbPackagesDat && cbPackagesDat) { LPBYTE pbPackagesPtr = pbPackagesDat; LPBYTE pbPackagesEnd = pbPackagesDat + cbPackagesDat; // Get the header. There is just Signature + NumberOfNames if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &Signature)) == NULL) return ERROR_BAD_FORMAT; if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &NumberOfNames)) == NULL) return ERROR_BAD_FORMAT; if(Signature != DIABLO3_PACKAGES_SIGNATURE || NumberOfNames == 0) return ERROR_BAD_FORMAT; // Create the map for fast search of the file name if(PackagesMap.Create(NumberOfNames, 0, 0, KeyIsString) == ERROR_SUCCESS) { const char * szPackageName = (const char *)pbPackagesPtr; // Go as long as there is something for(DWORD i = 0; i < NumberOfNames; i++) { // Get the file extension if((LPBYTE)szPackageName >= pbPackagesEnd) break; // Insert the file name to the map. The file extension is not included PackagesMap.InsertString(szPackageName, true); szPackageName = szPackageName + strlen(szPackageName) + 1; } } } return ERROR_SUCCESS; } DWORD Load(TCascStorage * hs, DIABLO3_DIRECTORY & RootDirectory) { CASC_PATH PathBuffer; DWORD dwErrCode; // Always parse the named entries first. They always point to a file. // These are entries with arbitrary names, and they do not belong to an asset dwErrCode = ParseDirectory_Phase1(hs, RootDirectory, PathBuffer, true); if(dwErrCode == ERROR_SUCCESS) { // The asset entries in the ROOT file don't contain file names, but indices. // To convert a file index to a file name, we need to load and parse the "Base\\CoreTOC.dat" file. dwErrCode = CreateMapOfFileIndices(hs, "Base\\CoreTOC.dat"); if(dwErrCode == ERROR_SUCCESS) { // The file "Base\Data_D3\PC\Misc\Packages.dat" contains the file names // (without level-0 and level-1 directory). // We can use these names for supplying the missing extensions CreateMapOfRealNames(hs, "Base\\Data_D3\\PC\\Misc\\Packages.dat"); // Now parse all folders and resolve the full names ParseDirectory_Phase2(hs); } // Free all stuff that was used during loading of the ROOT file FreeLoadingStuff(); } return dwErrCode; } void FreeLoadingStuff() { // Free the captured root sub-directories for(size_t i = 0; i < DIABLO3_MAX_SUBDIRS; i++) CASC_FREE(RootFolders[i].pbDirectoryData); // Free the package map PackagesMap.Free(); // Free the array of file indices CASC_FREE(pFileIndices); // Free the loaded CoreTOC.dat file CASC_FREE(pbCoreTocFile); // Free the loaded Packages.dat file CASC_FREE(pbPackagesDat); } // Array of root directory subdirectories DIABLO3_DIRECTORY RootFolders[DIABLO3_MAX_ROOT_FOLDERS]; // Array of DIABLO3_TOC_ENTRY structures, sorted by the file index // Used for converting FileIndex -> Asset+PlainName during loading PDIABLO3_CORE_TOC_ENTRY pFileIndices; LPBYTE pbCoreTocFile; LPBYTE pbCoreTocData; size_t nFileIndices; DWORD cbCoreTocFile; // Map for searching a real file extension CASC_MAP PackagesMap; LPBYTE pbPackagesDat; DWORD cbPackagesDat; }; //----------------------------------------------------------------------------- // Public functions DWORD RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) { TDiabloRoot * pRootHandler = NULL; DIABLO3_DIRECTORY RootDirectory; DWORD dwErrCode = ERROR_BAD_FORMAT; // Verify the header of the ROOT file if(TDiabloRoot::CaptureDirectoryData(RootDirectory, pbRootFile, cbRootFile) != NULL) { // Allocate the root handler object pRootHandler = new TDiabloRoot(); if(pRootHandler != NULL) { // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, RootDirectory); if(dwErrCode != ERROR_SUCCESS) { delete pRootHandler; pRootHandler = NULL; } } } // Assign the root directory (or NULL) and return error hs->pRootHandler = pRootHandler; return dwErrCode; } ================================================ FILE: deps/CascLib/src/CascRootFile_Install.cpp ================================================ /*****************************************************************************/ /* CascRootFile_Install.cpp Copyright (c) Ladislav Zezula 2018 */ /*---------------------------------------------------------------------------*/ /* Support for ROOT handler based on INSTALL manifest */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 21.05.19 1.00 Lad The first version of CascRootFile_Install.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Handler definitions for Starcraft I root file struct TRootHandler_Install : public TFileTreeRoot { public: TRootHandler_Install() : TFileTreeRoot(0) { // We have file names and return CKey as result of search dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY); } DWORD Load(TCascStorage * hs, CASC_INSTALL_HEADER InHeader, LPBYTE pbInstallFile, LPBYTE pbInstallEnd) { PCASC_CKEY_ENTRY pCKeyEntry; const char * szString; size_t nBitmapLength; size_t nFileCount = InHeader.EntryCount; // Skip the header pbInstallFile += InHeader.HeaderLength; // Skip the tags for (DWORD i = 0; i < InHeader.TagCount; i++) { szString = (const char *)pbInstallFile; nBitmapLength = GetTagBitmapLength(pbInstallFile, pbInstallEnd, InHeader.EntryCount); pbInstallFile = pbInstallFile + strlen(szString) + 1 + sizeof(USHORT) + nBitmapLength; } // Load the names and insert them to the root handler while(nFileCount > 0 && pbInstallFile < pbInstallEnd) { // File Name and CKey szString = (const char *)pbInstallFile; pbInstallFile += strlen(szString) + 1; // Verify whether it is a known entry pCKeyEntry = FindCKeyEntry_CKey(hs, pbInstallFile); pbInstallFile += MD5_HASH_SIZE + sizeof(DWORD); // Insert the FileName+CKey to the file tree if (pCKeyEntry != NULL) FileTree.InsertByName(pCKeyEntry, szString); nFileCount--; } return ERROR_SUCCESS; } }; //----------------------------------------------------------------------------- // Public functions DWORD CaptureInstallHeader(CASC_INSTALL_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData) { PFILE_INSTALL_HEADER pFileHeader = (PFILE_INSTALL_HEADER)pbFileData; // Check the signature ('DL') and version if (cbFileData < sizeof(FILE_INSTALL_HEADER) || pFileHeader->Magic != FILE_MAGIC_INSTALL || pFileHeader->Version != 1) return ERROR_BAD_FORMAT; // Note that we don't support CKey sizes greater than 0x10 in the INSTALL file if (pFileHeader->EKeyLength > MD5_HASH_SIZE) return ERROR_BAD_FORMAT; // Capture the header version 1 memset(&InHeader, 0, sizeof(CASC_INSTALL_HEADER)); InHeader.Magic = pFileHeader->Magic; InHeader.Version = pFileHeader->Version; InHeader.EKeyLength = pFileHeader->EKeyLength; InHeader.TagCount = ConvertBytesToInteger_2(pFileHeader->TagCount); InHeader.EntryCount = ConvertBytesToInteger_4(pFileHeader->EntryCount); InHeader.HeaderLength = sizeof(FILE_INSTALL_HEADER); return ERROR_SUCCESS; } DWORD RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbInstallFile, DWORD cbInstallFile) { CASC_INSTALL_HEADER InHeader; TRootHandler_Install * pRootHandler = NULL; DWORD dwErrCode = ERROR_BAD_FORMAT; // Capture the header of the DOWNLOAD file dwErrCode = CaptureInstallHeader(InHeader, pbInstallFile, cbInstallFile); if (dwErrCode == ERROR_SUCCESS) { // Allocate the root handler object pRootHandler = new TRootHandler_Install(); if (pRootHandler != NULL) { // Parse the entire install manifest dwErrCode = pRootHandler->Load(hs, InHeader, pbInstallFile, pbInstallFile + cbInstallFile); hs->pRootHandler = pRootHandler; } } return dwErrCode; } ================================================ FILE: deps/CascLib/src/CascRootFile_MNDX.cpp ================================================ /*****************************************************************************/ /* CascRootFile_MNDX.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Common functions for CascLib */ /* Note: "HOTS" refers to Play.exe, v2.5.0.29049 (Heroes of the Storm Alpha) */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 18.05.14 1.00 Lad The first version of CascRootFile_MNDX.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Local defines #define MNDX_MAR_SIGNATURE 0x0052414d // 'MAR\0' #define MAR_PACKAGE_NAMES 0 // MAR with package names only #define MAR_STRIPPED_NAMES 1 // MAR with names where packages were stripped #define MAR_FULL_NAMES 2 // MAR with full file names #define MAR_COUNT 3 // Maximum of 3 MAR files are supported #define MNDX_SEARCH_INITIALIZING 0 #define MNDX_SEARCH_SEARCHING 2 #define MNDX_SEARCH_FINISHED 4 #define MNDX_MAX_ENTRIES(type) (0xFFFFFFFF / sizeof(type)) #define MNDX_INVALID_SIZE_T ((size_t)(-1)) #define MNDX_LAST_CKEY_ENTRY 0x80000000 //----------------------------------------------------------------------------- // Local structures typedef union _SETBITS { struct { DWORD Lower08 : 8; // Number of set bits in the lower 1 byte DWORD Lower16 : 8; // Number of set bits in the lower 2 bytes DWORD Lower24 : 8; // Number of set bits in the lower 3 bytes DWORD Lower32 : 8; // Number of set bits in the 32-bit integer } u; DWORD SetBitsAll; // The total set bits mask } SETBITS, *PSETBITS; typedef struct _HASH_ENTRY { DWORD NodeIndex; // Index of the path node DWORD NextIndex; // ID of the first subnode in the hash table union { DWORD FragmentOffset; // Offset of the path fragment in the TPathFragmentTable DWORD ChildTableIndex; // Starting search index for the child database (if child database is present) char SingleChar; // If the upper 24 bits of the FragmentOffset is 0xFFFFFFFF, this single character }; // Otherwise --> Offset to the name fragment table } HASH_ENTRY, *PHASH_ENTRY; typedef struct _FILE_MNDX_HEADER { DWORD Signature; // 'MNDX' DWORD HeaderVersion; // Must be <= 2 DWORD FormatVersion; } FILE_MNDX_HEADER, *PFILE_MNDX_HEADER; typedef struct _MNDX_PACKAGE { char * szFileName; // Pointer to file name size_t nLength; // Length of the file name size_t nIndex; // Package index } MNDX_PACKAGE, *PMNDX_PACKAGE; // Root file entry for CASC storages with MNDX root file (Heroes of the Storm) // Corresponds to the in-file structure typedef struct _MNDX_CKEY_ENTRY { DWORD Flags; // High 8 bits: Flags, low 24 bits: package index BYTE CKey[MD5_HASH_SIZE]; // Content key for the file DWORD ContentSize; // Uncompressed file size, in bytes } MNDX_CKEY_ENTRY, *PMNDX_CKEY_ENTRY; typedef struct _FILE_MAR_INFO { DWORD MarIndex; DWORD MarDataSize; DWORD MarDataSizeHi; DWORD MarDataOffset; DWORD MarDataOffsetHi; } FILE_MAR_INFO, *PFILE_MAR_INFO; //----------------------------------------------------------------------------- // Local variables unsigned char table_1BA1818[0x800] = { 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x07, 0x07, 0x07, 0x01, 0x07, 0x02, 0x02, 0x01, 0x07, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x07, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x07, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x07, 0x06, 0x06, 0x01, 0x06, 0x02, 0x02, 0x01, 0x06, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x06, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x06, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x07, 0x07, 0x07, 0x01, 0x07, 0x02, 0x02, 0x01, 0x07, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x07, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x07, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x07, 0x06, 0x06, 0x01, 0x06, 0x02, 0x02, 0x01, 0x06, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x06, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x06, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x02, 0x07, 0x07, 0x07, 0x03, 0x07, 0x03, 0x03, 0x02, 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x02, 0x07, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x02, 0x07, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x02, 0x07, 0x06, 0x06, 0x03, 0x06, 0x03, 0x03, 0x02, 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x02, 0x06, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x02, 0x06, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x02, 0x07, 0x07, 0x07, 0x03, 0x07, 0x03, 0x03, 0x02, 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x02, 0x07, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x02, 0x07, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x02, 0x07, 0x06, 0x06, 0x03, 0x06, 0x03, 0x03, 0x02, 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x02, 0x06, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x02, 0x06, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x03, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x03, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x03, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x03, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x03, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x03, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x03, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x03, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 }; //----------------------------------------------------------------------------- // Local functions - Number of set bits in an integer // HOTS: inlined static SETBITS GetNumberOfSetBits(DWORD Value32) { SETBITS SetBits; Value32 = ((Value32 >> 1) & 0x55555555) + (Value32 & 0x55555555); Value32 = ((Value32 >> 2) & 0x33333333) + (Value32 & 0x33333333); Value32 = ((Value32 >> 4) & 0x0F0F0F0F) + (Value32 & 0x0F0F0F0F); SetBits.SetBitsAll = Value32 * 0x01010101; return SetBits; } static DWORD GetNumberOfSetBits32(DWORD Value32) { return GetNumberOfSetBits(Value32).u.Lower32; } static LPBYTE CaptureData(LPBYTE pbRootPtr, LPBYTE pbRootEnd, void * pvBuffer, size_t cbLength) { // Check whether there is enough data in the buffer if((pbRootPtr + cbLength) > pbRootEnd) return NULL; // Copy the data memcpy(pvBuffer, pbRootPtr, cbLength); return pbRootPtr + cbLength; } //----------------------------------------------------------------------------- // The TPathStop structure struct TPathStop { TPathStop() { LoBitsIndex = 0; field_4 = 0; Count = 0; HiBitsIndex_PathFragment = CASC_INVALID_INDEX; field_10 = 0xFFFFFFFF; } TPathStop(DWORD arg_0, DWORD arg_4, DWORD arg_8) { LoBitsIndex = arg_0; field_4 = arg_4; Count = arg_8; HiBitsIndex_PathFragment = CASC_INVALID_INDEX; field_10 = 0xFFFFFFFF; } DWORD LoBitsIndex; DWORD field_4; DWORD Count; DWORD HiBitsIndex_PathFragment; DWORD field_10; }; //----------------------------------------------------------------------------- // Basic array implementations class TByteStream { public: // HOTS: 01959990 TByteStream() { pbByteData = NULL; pvMappedFile = NULL; cbByteData = 0; field_C = 0; hFile = 0; hMap = 0; } // HOTS: 19599F0 template int GetBytes(size_t length, T ** Pointer) { // Is there enough bytes in the array? if(length > cbByteData) return ERROR_BAD_FORMAT; // Give the buffer to the caller Pointer[0] = (T *)(pbByteData); // Move pointers pbByteData += length; cbByteData -= length; return ERROR_SUCCESS; } int CopyBytes(void * value, size_t length) { // Is there enough bytes in the array? if(length > cbByteData) return ERROR_BAD_FORMAT; // Give the buffer to the caller memcpy(value, pbByteData, length); // Move pointers pbByteData += length; cbByteData -= length; return ERROR_SUCCESS; } // HOTS: 1959A60 int SkipBytes(size_t cbByteCount) { LPBYTE Pointer; return GetBytes(cbByteCount, &Pointer); } // HOTS: 1959AF0 int SetByteBuffer(LPBYTE pbNewByteData, size_t cbNewByteData) { if(pbNewByteData != NULL || cbNewByteData == 0) { pbByteData = pbNewByteData; cbByteData = cbNewByteData; return ERROR_SUCCESS; } return ERROR_INVALID_PARAMETER; } // HOTS: 1957160 template DWORD GetValue(T & Value) { T * Pointer; DWORD dwErrCode; dwErrCode = GetBytes(sizeof(T), (LPBYTE *)(&Pointer)); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; Value = Pointer[0]; return ERROR_SUCCESS; } // Retrieves the item count in the array template DWORD GetArrayItemCount(DWORD & ArraySize, DWORD & ItemCount) { ULONGLONG ByteCount; DWORD dwErrCode; // The first 8 bytes is the byte size of the array dwErrCode = GetValue(ByteCount); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Extract the number of bytes if(ByteCount > 0xFFFFFFFF || (ByteCount % sizeof(T)) != 0) return ERROR_BAD_FORMAT; // Give the result to the caller ItemCount = (DWORD)(ByteCount / sizeof(T)); ArraySize = (DWORD)(ByteCount); return ERROR_SUCCESS; } // HOTS: 1957190: // HOTS: 19571E0: // HOTS: 1957230: // HOTS: 1957280: template DWORD GetArray(T ** Pointer, size_t ItemCount) { DWORD dwErrCode = ERROR_SUCCESS; // Verify parameters if(Pointer == NULL && ItemCount != 0) return ERROR_INVALID_PARAMETER; if(ItemCount > MNDX_MAX_ENTRIES(T)) return ERROR_NOT_ENOUGH_MEMORY; // Allocate bytes for the array if (Pointer != NULL) { Pointer[0] = CASC_ALLOC(ItemCount); if (Pointer[0] == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Get the pointer to the array dwErrCode = CopyBytes(Pointer[0], sizeof(T) * ItemCount); } return dwErrCode; } LPBYTE pbByteData; void * pvMappedFile; size_t cbByteData; DWORD field_C; HANDLE hFile; HANDLE hMap; }; //----------------------------------------------------------------------------- // TGenericArray interface/implementation template class TGenericArray { public: TGenericArray() { ItemArray = NULL; ItemCount = 0; MaxItemCount = 0; bIsValidArray = false; } ~TGenericArray() { CASC_FREE(ItemArray); } T & operator[] (size_t index) { assert(index < ItemCount); return ItemArray[index]; } // HOTS: 1957090 (SetDwordsValid) // HOTS: 19570B0 (SetBaseValsValid) // HOTS: 19570D0 (? SetBitsValid ?) // HOTS: 19570F0 (SetPathFragmentsValid) int SetArrayValid() { if(bIsValidArray != 0) return ERROR_ALREADY_EXISTS; bIsValidArray = true; return ERROR_SUCCESS; } // HOTS: 19575A0 (char) // HOTS: 1957600 (TPathStop) void SetMaxItems(DWORD NewMaxItemCount) { T * OldArray = ItemArray; T * NewArray; // Allocate new data buffer NewArray = CASC_ALLOC(NewMaxItemCount); if(NewArray != NULL) { // Copy the old items to the buffer for(size_t i = 0; i < ItemCount; i++) { NewArray[i] = ItemArray[i]; } } ItemArray = NewArray; MaxItemCount = NewMaxItemCount; CASC_FREE(OldArray); } // HOTS: 19575A0 (char) // HOTS: 1957600 (TPathStop) void SetMaxItemsIf(DWORD NewMaxItemCount) { if(NewMaxItemCount > MaxItemCount) { if(MaxItemCount > (NewMaxItemCount / 2)) { if(MaxItemCount <= (MNDX_MAX_ENTRIES(T) / 2)) NewMaxItemCount = MaxItemCount + MaxItemCount; else NewMaxItemCount = MNDX_MAX_ENTRIES(T); } SetMaxItems(NewMaxItemCount); } } // HOTS: inline // HOTS: 1958330 void Insert(T NewItem) { // Make sure we have enough capacity for the new item SetMaxItemsIf(ItemCount + 1); // Put the character to the slot that has been reserved ItemArray[ItemCount++] = NewItem; } // HOTS: 19583A0 void GrowArray(DWORD NewItemCount) { DWORD OldMaxItemCount = MaxItemCount; // Make sure we have enough capacity for new items SetMaxItemsIf(NewItemCount); // Initialize the newly inserted items for(DWORD i = OldMaxItemCount; i < NewItemCount; i++) { ItemArray[i] = T(); } ItemCount = NewItemCount; } // HOTS: 1957440 // HOTS: 19574E0 // HOTS: 1957690 // HOTS: 1957700 // HOTS: 195A220 // HOTS: 1958580 DWORD LoadFromStream(TByteStream & InStream) { DWORD NumberOfBytes; DWORD dwErrCode; // Get and verify the number of items dwErrCode = InStream.GetArrayItemCount(NumberOfBytes, ItemCount); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Get the pointer to the array dwErrCode = InStream.GetArray(&ItemArray, ItemCount); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; return SetArrayValid(); } T * ItemArray; DWORD ItemCount; // Number of items in the array DWORD MaxItemCount; // Capacity of the array bool bIsValidArray; }; class TBitEntryArray : public TGenericArray { public: TBitEntryArray() : TGenericArray() { BitsPerEntry = 0; EntryBitMask = 0; TotalEntries = 0; } ~TBitEntryArray() {} DWORD GetItem(DWORD EntryIndex) { DWORD dwItemIndex = (EntryIndex * BitsPerEntry) >> 0x05; DWORD dwStartBit = (EntryIndex * BitsPerEntry) & 0x1F; DWORD dwEndBit = dwStartBit + BitsPerEntry; DWORD dwResult; // If the end bit index is greater than 32, // we also need to load from the next 32-bit item if(dwEndBit > 0x20) { dwResult = (ItemArray[dwItemIndex + 1] << (0x20 - dwStartBit)) | (ItemArray[dwItemIndex] >> dwStartBit); } else { dwResult = ItemArray[dwItemIndex] >> dwStartBit; } // Now we also need to mask the result by the bit mask return dwResult & EntryBitMask; } DWORD LoadBitsFromStream(TByteStream & InStream) { ULONGLONG Value64 = 0; DWORD dwErrCode; dwErrCode = LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = InStream.GetValue(BitsPerEntry); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; if(BitsPerEntry > 0x20) return ERROR_BAD_FORMAT; dwErrCode = InStream.GetValue(EntryBitMask); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = InStream.GetValue(Value64); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; if(Value64 > 0xFFFFFFFF) return ERROR_BAD_FORMAT; TotalEntries = (DWORD)Value64; assert((BitsPerEntry * TotalEntries) / 32 <= ItemCount); return ERROR_SUCCESS; } DWORD BitsPerEntry; DWORD EntryBitMask; DWORD TotalEntries; }; //----------------------------------------------------------------------------- // TSparseArray functions #define INDEX_TO_GROUP(val) (val >> 9) #define GROUP_TO_INDEX(grp) (grp << 9) // For each 0x200-th bit, this contains information about amount of "1" bits typedef struct _BASEVALS { DWORD BaseValue200; // Item value of every 0x200-th item DWORD AddValue40 : 7; // For each 0x40 items (above the base200), DWORD AddValue80 : 8; // we have extra shortcut to the item value DWORD AddValueC0 : 8; // that is to be added to BaseValue200 DWORD AddValue100 : 9; DWORD AddValue140 : 9; DWORD AddValue180 : 9; DWORD AddValue1C0 : 9; DWORD __xalignment : 5; // Filling } BASEVALS, *PBASEVALS; class TSparseArray { public: TSparseArray() { TotalItemCount = 0; ValidItemCount = 0; } // HOTS: 1958630 DWORD LoadFromStream(TByteStream & InStream) { DWORD total_count = 0; DWORD valid_count = 0; DWORD dwErrCode; dwErrCode = ItemBits.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = InStream.GetValue(total_count); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = InStream.GetValue(valid_count); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; if(valid_count > total_count) return ERROR_FILE_CORRUPT; TotalItemCount = total_count; ValidItemCount = valid_count; dwErrCode = BaseVals.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = IndexToItem0.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = IndexToItem1.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; return ERROR_SUCCESS; } // Returns true if the array is empty bool IsEmpty() { return (TotalItemCount == 0); } // Returns true if the item at n-th position is present bool IsItemPresent(size_t index) { // (index >> 0x05) gives the DWORD, (1 << (ItemIndex & 0x1F)) gives the bit return (ItemBits[index >> 0x05] & (1 << (index & 0x1F))) ? true : false; } // Retrieves the value of the n-th item in the sparse array. // Note that for items that are not present, the value is equal // to the nearest lower present value DWORD GetItemValueAt(size_t index) { BASEVALS & SetBitsCount = BaseVals[index >> 0x09]; DWORD IntValue; DWORD BitMask; // // Since we don't want to count bits for the entire array, // there are item value shortcuts every 0x200 items, // and then every 0x40 items above the 0x200 base // // 1) We have base value for every 0x200-th item IntValue = SetBitsCount.BaseValue200; // 2) Add the base value for each 0x40-th item above the 0x200 base switch(((index >> 0x06) & 0x07) - 1) { case 0: // Add the 1st value (7 bits) IntValue += SetBitsCount.AddValue40; break; case 1: // Add the 2nd value (8 bits) IntValue += SetBitsCount.AddValue80; break; case 2: // Add the 3rd value (8 bits) IntValue += SetBitsCount.AddValueC0; break; case 3: // Add the 4th value (9 bits) IntValue += SetBitsCount.AddValue100; break; case 4: // Add the 5th value (9 bits) IntValue += SetBitsCount.AddValue140; break; case 5: // Add the 6th value (9 bits) IntValue += SetBitsCount.AddValue180; break; case 6: // Add the 7th value (9 bits) IntValue += SetBitsCount.AddValue1C0; break; } // 3) Count the bits of the higher DWORD, if the index 0x20 - 0x30 above the 0x200 base if(index & 0x20) IntValue += GetNumberOfSetBits32(ItemBits[(index >> 0x05) - 1]); // 4) Count the bits in the current DWORD (masked by bit index mask) BitMask = (1 << (index & 0x1F)) - 1; return IntValue + GetNumberOfSetBits32(ItemBits[index >> 0x05] & BitMask); } DWORD FindGroup_Items0(DWORD index) { // Setup the group range to search DWORD minGroup = (IndexToItem0[INDEX_TO_GROUP(index) + 0]) >> 9; DWORD maxGroup = (IndexToItem0[INDEX_TO_GROUP(index) + 1] + 0x1FF) >> 9; // Search the groups and find the BASEVALS structure // For spans less than 10 groups, use sequential search, otherwise binary search. if ((maxGroup - minGroup) < 10) { // HOTS: 1959CF7 while (index >= GROUP_TO_INDEX(minGroup) - BaseVals[minGroup + 1].BaseValue200 + 0x200) { // HOTS: 1959D14 minGroup++; } } else { // HOTS: 1959D2E while ((minGroup + 1) < maxGroup) { // HOTS: 1959D38 DWORD middleValue = (maxGroup + minGroup) >> 1; if (index < (maxGroup << 0x09) - BaseVals[maxGroup].BaseValue200) { // HOTS: 01959D4B maxGroup = middleValue; } else { // HOTS: 1959D50 minGroup = middleValue; } } } return minGroup; } DWORD FindGroup_Items1(DWORD index) { DWORD groupIndex = (index >> 0x09); DWORD startValue = IndexToItem1[groupIndex] >> 9; DWORD nextValue = (IndexToItem1[groupIndex + 1] + 0x1FF) >> 9; // Find the BASEVALS structure which the start index belongs to // For less than 10 values, use sequential search. Otherwise, use binary search if ((nextValue - startValue) < 10) { // HOTS: 01959F94 while (index >= BaseVals[startValue + 1].BaseValue200) { // HOTS: 1959FA3 startValue++; } } else { // Binary search (HOTS: 1959FAD) if ((startValue + 1) < nextValue) { // HOTS: 1959FB4 DWORD middleValue = (nextValue + startValue) >> 1; if (index < BaseVals[middleValue].BaseValue200) { // HOTS: 1959FC4 nextValue = middleValue; } else { // HOTS: 1959FC8 startValue = middleValue; } } } return startValue; } // Returns the value of Item0[index] (HOTS: 1959CB0) DWORD GetItem0(DWORD index) { SETBITS zeroBits; DWORD groupIndex; DWORD dwordIndex; DWORD itemIndex; DWORD bitGroup; DWORD edx = index; #ifdef _DEBUG //if (TotalItemCount > 0x200) //{ // FILE * fp = fopen("e:\\Ladik\\Appdir\\CascLib\\doc\\mndx-sparse-array.txt", "wt"); // Dump(fp); // fclose(fp); //} #endif // If the index is at begin of the group, we just return the start value if ((index & 0x1FF) == 0) return IndexToItem0[INDEX_TO_GROUP(index)]; // Find the group where the index belongs to groupIndex = FindGroup_Items0(index); // HOTS: 1959D5F edx += BaseVals[groupIndex].BaseValue200 - (groupIndex << 0x09); dwordIndex = (groupIndex << 4); if (edx < 0x100 - BaseVals[groupIndex].AddValue100) { // HOTS: 1959D8C if (edx < 0x80 - BaseVals[groupIndex].AddValue80) { // HOTS: 01959DA2 if (edx >= 0x40 - BaseVals[groupIndex].AddValue40) { // HOTS: 01959DB7 dwordIndex += 2; edx = edx + BaseVals[groupIndex].AddValue40 - 0x40; } } else { // HOTS: 1959DC0 if (edx < 0xC0 - BaseVals[groupIndex].AddValueC0) { // HOTS: 1959DD3 dwordIndex += 4; edx = edx + BaseVals[groupIndex].AddValue80 - 0x80; } else { // HOTS: 1959DD3 dwordIndex += 6; edx = edx + BaseVals[groupIndex].AddValueC0 - 0xC0; } } } else { // HOTS: 1959DE8 if (edx < 0x180 - BaseVals[groupIndex].AddValue180) { // HOTS: 01959E00 if (edx < 0x140 - BaseVals[groupIndex].AddValue140) { // HOTS: 1959E11 dwordIndex += 8; edx = edx + BaseVals[groupIndex].AddValue100 - 0x100; } else { // HOTS: 1959E1D dwordIndex += 10; edx = edx + BaseVals[groupIndex].AddValue140 - 0x140; } } else { // HOTS: 1959E29 if (edx < 0x1C0 - BaseVals[groupIndex].AddValue1C0) { // HOTS: 1959E3D dwordIndex += 12; edx = edx + BaseVals[groupIndex].AddValue180 - 0x180; } else { // HOTS: 1959E49 dwordIndex += 14; edx = edx + BaseVals[groupIndex].AddValue1C0 - 0x1C0; } } } // HOTS: 1959E53: // Calculate the number of bits set in the value of "bitGroup" bitGroup = ~ItemBits[dwordIndex]; zeroBits = GetNumberOfSetBits(bitGroup); if (edx >= zeroBits.u.Lower32) { // HOTS: 1959ea4 bitGroup = ~ItemBits[++dwordIndex]; edx = edx - zeroBits.u.Lower32; zeroBits = GetNumberOfSetBits(bitGroup); } // Re-calculate the item index itemIndex = (dwordIndex << 0x05); // HOTS: 1959eea if (edx < zeroBits.u.Lower16) { // HOTS: 1959EFC if (edx >= zeroBits.u.Lower08) { // HOTS: 1959F05 bitGroup >>= 0x08; itemIndex += 0x08; edx -= zeroBits.u.Lower08; } } else { // HOTS: 1959F0D if (edx < zeroBits.u.Lower24) { // HOTS: 1959F19 bitGroup >>= 0x10; itemIndex += 0x10; edx -= zeroBits.u.Lower16; } else { // HOTS: 1959F23 bitGroup >>= 0x18; itemIndex += 0x18; edx -= zeroBits.u.Lower24; } } // HOTS: 1959f2b edx = edx << 0x08; bitGroup = bitGroup & 0xFF; // BUGBUG: Possible buffer overflow here. Happens when dwItemIndex >= 0x9C. // The same happens in Heroes of the Storm (build 29049), so I am not sure // if this is a bug or a case that never happens assert((bitGroup + edx) < sizeof(table_1BA1818)); return table_1BA1818[bitGroup + edx] + itemIndex; } DWORD GetItem1(DWORD index) { SETBITS setBits; DWORD distFromBase; DWORD groupIndex; DWORD dwordIndex; DWORD itemIndex; DWORD bitGroup; // If the index is at begin of the group, we just return the start value if ((index & 0x1FF) == 0) return IndexToItem1[INDEX_TO_GROUP(index)]; // Find the group where the index belongs to groupIndex = FindGroup_Items1(index); // Calculate the base200 dword index (HOTS: 1959FD4) distFromBase = index - BaseVals[groupIndex].BaseValue200; dwordIndex = groupIndex << 0x04; // Calculate the dword index including the sub-checkpoint if (distFromBase < BaseVals[groupIndex].AddValue100) { // HOTS: 1959FF1 if (distFromBase < BaseVals[groupIndex].AddValue80) { // HOTS: 0195A000 if (distFromBase >= BaseVals[groupIndex].AddValue40) { // HOTS: 195A007 distFromBase = distFromBase - BaseVals[groupIndex].AddValue40; dwordIndex += 2; } } else { // HOTS: 195A00E if (distFromBase < BaseVals[groupIndex].AddValueC0) { // HOTS: 195A01A distFromBase = distFromBase - BaseVals[groupIndex].AddValue80; dwordIndex += 4; } else { // HOTS: 195A01F distFromBase = distFromBase - BaseVals[groupIndex].AddValueC0; dwordIndex += 6; } } } else { // HOTS: 195A026 if (distFromBase < BaseVals[groupIndex].AddValue180) { // HOTS: 195A037 if (distFromBase < BaseVals[groupIndex].AddValue140) { // HOTS: 195A041 distFromBase = distFromBase - BaseVals[groupIndex].AddValue100; dwordIndex += 8; } else { // HOTS: 195A048 distFromBase = distFromBase - BaseVals[groupIndex].AddValue140; dwordIndex += 10; } } else { // HOTS: 195A04D if (distFromBase < BaseVals[groupIndex].AddValue1C0) { // HOTS: 195A05A distFromBase = distFromBase - BaseVals[groupIndex].AddValue180; dwordIndex += 12; } else { // HOTS: 195A061 distFromBase = distFromBase - BaseVals[groupIndex].AddValue1C0; dwordIndex += 14; } } } // HOTS: 195A066 bitGroup = ItemBits[dwordIndex]; setBits = GetNumberOfSetBits(bitGroup); // Get total number of set bits in the bit group if (distFromBase >= setBits.u.Lower32) { // HOTS: 195A0B2 bitGroup = ItemBits[++dwordIndex]; distFromBase = distFromBase - setBits.u.Lower32; setBits = GetNumberOfSetBits(bitGroup); } // Calculate the item index itemIndex = (dwordIndex << 0x05); // Get the number of set bits in the lower word (HOTS: 195A0F6) if (distFromBase < setBits.u.Lower16) { // HOTS: 195A111 if (distFromBase >= setBits.u.Lower08) { // HOTS: 195A111 itemIndex = itemIndex + 0x08; bitGroup = bitGroup >> 0x08; distFromBase = distFromBase - setBits.u.Lower08; } } else { // HOTS: 195A119 if (distFromBase < setBits.u.Lower24) { // HOTS: 195A125 bitGroup = bitGroup >> 0x10; itemIndex = itemIndex + 0x10; distFromBase = distFromBase - setBits.u.Lower16; } else { // HOTS: 195A12F bitGroup = bitGroup >> 0x18; itemIndex = itemIndex + 0x18; distFromBase = distFromBase - setBits.u.Lower24; } } bitGroup = bitGroup & 0xFF; distFromBase = distFromBase << 0x08; // BUGBUG: Potential buffer overflow // Happens in Heroes of the Storm when index == 0x5B assert((bitGroup + distFromBase) < sizeof(table_1BA1818)); return table_1BA1818[bitGroup + distFromBase] + itemIndex; } #ifdef _DEBUG void Dump(FILE * fp) { size_t * ArrayNormal; size_t * ArrayInvert; size_t IndexNormal = 0; size_t IndexInvert = 0; // Output numbers of set bits for every 0x200-th item fprintf(fp, "Number of set bits for every 0x200-th index\n" "========================================================\n" " Index Base200h +40 +80 +C0 +100 +140 +180 +1C0\n" "--------------------------------------------------------\n"); for (size_t i = 0; i < BaseVals.ItemCount; i++) { fprintf(fp, "[%08zX]: %08x %04x %04x %04x %04x %04x %04x %04x\n", GROUP_TO_INDEX(i), BaseVals[i].BaseValue200, BaseVals[i].AddValue40, BaseVals[i].AddValue80, BaseVals[i].AddValueC0, BaseVals[i].AddValue100, BaseVals[i].AddValue140, BaseVals[i].AddValue180, BaseVals[i].AddValue1C0); } fprintf(fp, "\n"); // Output values of Item1 and Item0 for every 0x200-th index fprintf(fp, "Item0 and Item1 for every 0x200-th index\n" "========================================\n" " Index Item0 Item1\n" "-----------------------------\n"); for (size_t i = 0; i < IndexToItem0.ItemCount; i++) { fprintf(fp, "[%08zX]: %08x %08x\n", GROUP_TO_INDEX(i), IndexToItem0[i], IndexToItem1[i]); } fprintf(fp, "\n"); // Output values of Item1 and Item0 for every index ArrayNormal = new size_t[TotalItemCount]; ArrayInvert = new size_t[TotalItemCount]; if (ArrayNormal && ArrayInvert) { // Invalidate both arrays memset(ArrayNormal, 0xFF, TotalItemCount * sizeof(size_t)); memset(ArrayInvert, 0xFF, TotalItemCount * sizeof(size_t)); // Load the both arrays for (size_t i = 0; i < TotalItemCount; i++) { if (IsItemPresent(i)) ArrayNormal[IndexNormal++] = i; else ArrayInvert[IndexInvert++] = i; } // Output both arrays fprintf(fp, "Item0 and Item1 for every index\n" "========================================\n" " Index Item0 Item1\n" "-----------------------------\n"); for (size_t i = 0; i < TotalItemCount; i++) { char NormalValue[0x20]; char InvertValue[0x20]; if (ArrayNormal[i] == MNDX_INVALID_SIZE_T && ArrayInvert[i] == MNDX_INVALID_SIZE_T) break; fprintf(fp, "[%08zX]: %8s %8s\n", i, DumpValue(InvertValue, _countof(InvertValue), ArrayInvert[i]), DumpValue(NormalValue, _countof(NormalValue), ArrayNormal[i])); } fprintf(fp, "\n"); } // Free both arrays delete[] ArrayNormal; delete[] ArrayInvert; // Output array of all values fprintf(fp, "Item List: Index -> Value\n==========================\n"); for (size_t i = 0; i < TotalItemCount; i++) { if (IsItemPresent(i)) { fprintf(fp, "[%08zX]: %08x\n", i, GetItemValueAt(i)); } else { fprintf(fp, "[%08zX]: NOT PRESENT\n", i); } } fprintf(fp, "\n"); } char * DumpValue(char * szBuffer, size_t cchBuffer, size_t value) { CascStrPrintf(szBuffer, cchBuffer, (value != MNDX_INVALID_SIZE_T) ? "%08zX" : " - ", value); return szBuffer; } #endif TGenericArray ItemBits; // A bit array for each item. 1 if the item is present. size_t TotalItemCount; // Total number of items in the array size_t ValidItemCount; // Number of present items in the array TGenericArray BaseVals; // For each 0x200-th item, this contains the number of set bits up to that 0x200-th item TGenericArray IndexToItem0; // Mapping of index to invert item. An "invert" item is an item whose bit in "ItemBits" is zero. TGenericArray IndexToItem1; // Mapping of index to normal item. An "normal" item is an item whose bit in "ItemBits" is set. }; //----------------------------------------------------------------------------- // TStruct40 functions class TStruct40 { public: TStruct40() { NodeIndex = 0; ItemCount = 0; PathLength = 0; SearchPhase = MNDX_SEARCH_INITIALIZING; } // HOTS: 19586B0 void BeginSearch() { // HOTS: 19586BD PathBuffer.ItemCount = 0; PathBuffer.SetMaxItemsIf(0x40); // HOTS: 19586E1 // Set the new item count PathStops.GrowArray(0); PathStops.SetMaxItemsIf(4); PathLength = 0; NodeIndex = 0; ItemCount = 0; SearchPhase = MNDX_SEARCH_SEARCHING; } DWORD CalcHashValue(const char * szPath) { return (BYTE)(szPath[PathLength]) ^ (NodeIndex << 0x05) ^ NodeIndex; } TGenericArray PathStops; // Array of path checkpoints TGenericArray PathBuffer; // Buffer for building a file name DWORD NodeIndex; // ID of a path node being searched; starting with 0 DWORD PathLength; // Length of the path in the PathBuffer DWORD ItemCount; DWORD SearchPhase; // 0 = initializing, 2 = searching, 4 = finished }; //----------------------------------------------------------------------------- // Local functions - TMndxSearch class TMndxSearch { public: // HOTS: 01956EE0 TMndxSearch() { szSearchMask = NULL; cchSearchMask = 0; szFoundPath = NULL; cchFoundPath = 0; nIndex = 0; } // HOTS: 01956F00 ~TMndxSearch() {} // HOTS: 01956E70 int SetSearchMask( const char * szNewSearchMask, size_t cchNewSearchMask) { if(szSearchMask == NULL && cchSearchMask != 0) return ERROR_INVALID_PARAMETER; Struct40.SearchPhase = MNDX_SEARCH_INITIALIZING; szSearchMask = szNewSearchMask; cchSearchMask = cchNewSearchMask; return ERROR_SUCCESS; } TStruct40 Struct40; const char * szSearchMask; // Search mask without wildcards size_t cchSearchMask; // Length of the search mask const char * szFoundPath; // Found path name size_t cchFoundPath; // Length of the found path name DWORD nIndex; // Index of the file name }; //----------------------------------------------------------------------------- // TPathFragmentTable class. This class implements table of the path fragments. // These path fragments can either by terminated by zeros (ASCIIZ) // or can be marked by the external "PathMarks" structure class TPathFragmentTable { public: // HOTS: 0195A290 TPathFragmentTable() {} // HOTS: inlined ~TPathFragmentTable() {} // HOTS: 195A180 bool ComparePathFragment(TMndxSearch * pSearch, size_t nFragmentOffset) { TStruct40 * pStruct40 = &pSearch->Struct40; // Do we have path fragment separators in an external structure? if(PathMarks.IsEmpty()) { // Keep searching as long as the name matches with the fragment while(PathFragments[nFragmentOffset] == pSearch->szSearchMask[pStruct40->PathLength]) { // Move to the next character pStruct40->PathLength++; nFragmentOffset++; // Is it the end of the fragment or end of the path? if(PathFragments[nFragmentOffset] == 0) return true; if(pStruct40->PathLength >= pSearch->cchSearchMask) return false; } return false; } else { // Keep searching as long as the name matches with the fragment while(PathFragments[nFragmentOffset] == pSearch->szSearchMask[pStruct40->PathLength]) { // Move to the next character pStruct40->PathLength++; // Is it the end of the path fragment? if(PathMarks.IsItemPresent(nFragmentOffset++)) return true; if(nFragmentOffset >= pSearch->cchSearchMask) return false; } return false; } } // HOTS: 195A3F0 void CopyPathFragment(TMndxSearch * pSearch, size_t nFragmentOffset) { TStruct40 * pStruct40 = &pSearch->Struct40; // Do we have path fragment separators in an external structure? if (PathMarks.IsEmpty()) { // HOTS: 195A40C while (PathFragments[nFragmentOffset] != 0) { // Insert the character to the path being built pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]); } } else { // HOTS: 195A4B3 while(!PathMarks.IsItemPresent(nFragmentOffset)) { // Insert the character to the path being built pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]); } } } // HOTS: 195A570 bool CompareAndCopyPathFragment(TMndxSearch * pSearch, size_t nFragmentOffset) { TStruct40 * pStruct40 = &pSearch->Struct40; // Do we have path fragment separators in an external structure? if(PathMarks.IsEmpty()) { // Keep copying as long as we don't reach the end of the search mask while(pStruct40->PathLength < pSearch->cchSearchMask) { // HOTS: 195A5A0 if(PathFragments[nFragmentOffset] != pSearch->szSearchMask[pStruct40->PathLength]) return false; // HOTS: 195A5B7 pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]); pStruct40->PathLength++; // If we found the end of the fragment, return success if(PathFragments[nFragmentOffset] == 0) return true; } // HOTS: 195A660 // Now we need to copy the rest of the fragment while(PathFragments[nFragmentOffset] != 0) { pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset]); nFragmentOffset++; } } else { // Keep copying as long as we don't reach the end of the search mask while(nFragmentOffset < pSearch->cchSearchMask) { if(PathFragments[nFragmentOffset] != pSearch->szSearchMask[pStruct40->PathLength]) return false; pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset]); pStruct40->PathLength++; // If we found the end of the fragment, return success if(PathMarks.IsItemPresent(nFragmentOffset++)) return true; } // Now we need to copy the rest of the fragment while(!PathMarks.IsItemPresent(nFragmentOffset)) { // HOTS: 195A7A6 pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]); } } return true; } // HOTS: 0195A820 DWORD LoadFromStream(TByteStream & InStream) { DWORD dwErrCode; dwErrCode = PathFragments.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; return PathMarks.LoadFromStream(InStream); } TGenericArray PathFragments; TSparseArray PathMarks; }; //----------------------------------------------------------------------------- // TStruct10 functions class TStruct10 { public: TStruct10() { field_0 = 0x03; field_4 = 0x200; field_8 = 0x1000; field_C = 0x20000; } // HOTS: 1956FD0 int sub_1956FD0(DWORD dwBitMask) { switch(dwBitMask & 0xF80) { case 0x00: field_4 = 0x200; return ERROR_SUCCESS; case 0x80: field_4 = 0x80; return ERROR_SUCCESS; case 0x100: field_4 = 0x100; return ERROR_SUCCESS; case 0x200: field_4 = 0x200; return ERROR_SUCCESS; case 0x400: field_4 = 0x400; return ERROR_SUCCESS; case 0x800: field_4 = 0x800; return ERROR_SUCCESS; } return ERROR_INVALID_PARAMETER; } // HOTS: 1957050 int sub_1957050(DWORD dwBitMask) { switch(dwBitMask & 0xF0000) { case 0x00: field_C = 0x20000; return ERROR_SUCCESS; case 0x10000: field_C = 0x10000; return ERROR_SUCCESS; case 0x20000: field_C = 0x20000; return ERROR_SUCCESS; } return ERROR_INVALID_PARAMETER; } // HOTS: 19572E0 DWORD sub_19572E0(DWORD dwBitMask) { DWORD dwSubMask; DWORD dwErrCode; if(dwBitMask & 0xFFF00000) return ERROR_INVALID_PARAMETER; dwSubMask = dwBitMask & 0x7F; if(dwSubMask) field_0 = dwSubMask; dwErrCode = sub_1956FD0(dwBitMask); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwSubMask = dwBitMask & 0xF000; if(dwSubMask == 0 || dwSubMask == 0x1000) { field_8 = 0x1000; return sub_1957050(dwBitMask); } if(dwSubMask == 0x2000) { field_8 = 0x2000; return sub_1957050(dwBitMask); } return ERROR_INVALID_PARAMETER; } // HOTS: 1957800 int sub_1957800(DWORD dwBitMask) { return sub_19572E0(dwBitMask); } DWORD field_0; DWORD field_4; DWORD field_8; DWORD field_C; }; //----------------------------------------------------------------------------- // TFileNameDatabase interface/implementation class TFileNameDatabase { public: // HOTS: 01958730 TFileNameDatabase() { HashTableMask = 0; field_214 = 0; pChildDB = NULL; } ~TFileNameDatabase() { delete pChildDB; } // Returns nonzero if the name fragment match is a single-char match bool IsPathFragmentSingleChar(HASH_ENTRY * pHashEntry) { return ((pHashEntry->FragmentOffset & 0xFFFFFF00) == 0xFFFFFF00); } // Returns true if the given collision path fragment is a string (aka more than 1 char) bool IsPathFragmentString(size_t index) { return CollisionHiBitsIndexes.IsItemPresent(index); } // HOTS: 1957350, inlined DWORD GetPathFragmentOffset1(DWORD index_lobits) { DWORD index_hibits = CollisionHiBitsIndexes.GetItemValueAt(index_lobits); return (HiBitsTable.GetItem(index_hibits) << 0x08) | LoBitsTable[index_lobits]; } // Retrieves fragment_offset/subtable_index of the path fragment, with check for starting value DWORD GetPathFragmentOffset2(DWORD & index_hibits, DWORD index_lobits) { // If the hi-bits index is invalid, we need to get its starting value if (index_hibits == CASC_INVALID_INDEX) { /* printf("\n"); for (DWORD i = 0; i < CollisionHiBitsIndexes.TotalItemCount; i++) { if (CollisionHiBitsIndexes.IsItemPresent(i)) printf("[%02X] = %02X\n", i, CollisionHiBitsIndexes.GetIntValueAt(i)); else printf("[%02X] = NOT_PRESENT\n", i); } */ index_hibits = CollisionHiBitsIndexes.GetItemValueAt(index_lobits); } else { index_hibits++; } // Now we use both NodeIndex and HiBits index for retrieving the path fragment index return (HiBitsTable.GetItem(index_hibits) << 0x08) | LoBitsTable[index_lobits]; } // HOTS: 1956DA0 DWORD Load(LPBYTE pbMarData, size_t cbMarData) { TByteStream ByteStream; DWORD dwSignature; DWORD dwErrCode; if(pbMarData == NULL && cbMarData != 0) return ERROR_INVALID_PARAMETER; dwErrCode = ByteStream.SetByteBuffer(pbMarData, cbMarData); if(dwErrCode == ERROR_SUCCESS) { // Get pointer to MAR signature dwErrCode = ByteStream.GetValue(dwSignature); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Verify the signature if(dwSignature != MNDX_MAR_SIGNATURE) return ERROR_BAD_FORMAT; // HOTS: 1956E11 dwErrCode = LoadFromStream(ByteStream); } return dwErrCode; } // HOTS: 19584B0 int SetChildDatabase(TFileNameDatabase * pNewDB) { if(pNewDB != NULL && pChildDB == pNewDB) return ERROR_INVALID_PARAMETER; if(pChildDB != NULL) delete pChildDB; pChildDB = pNewDB; return ERROR_SUCCESS; } // HOTS: 1957970 bool ComparePathFragment(TMndxSearch * pSearch) { TStruct40 * pStruct40 = &pSearch->Struct40; PHASH_ENTRY pHashEntry; DWORD ColTableIndex; DWORD HiBitsIndex; DWORD NodeIndex; // Calculate the item hash from the current char and fragment ID NodeIndex = pStruct40->CalcHashValue(pSearch->szSearchMask) & HashTableMask; pHashEntry = &HashTable[NodeIndex]; // Does the hash value ID match? if(pHashEntry->NodeIndex == pStruct40->NodeIndex) { // Check if there is single character match if (!IsPathFragmentSingleChar(pHashEntry)) { // Check if there is a name fragment match if (pChildDB != NULL) { if (!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) return false; } else { if (!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset)) return false; } } else { pStruct40->PathLength++; } pStruct40->NodeIndex = pHashEntry->NextIndex; return true; } // // Conflict: Multiple node IDs give the same table index // // HOTS: 1957A0E ColTableIndex = CollisionTable.GetItem0(pStruct40->NodeIndex) + 1; pStruct40->NodeIndex = (ColTableIndex - pStruct40->NodeIndex - 1); HiBitsIndex = CASC_INVALID_INDEX; // HOTS: 1957A41: while(CollisionTable.IsItemPresent(ColTableIndex)) { // HOTS: 1957A41 // Check if the low 8 bits if the fragment offset contain a single character // or an offset to a name fragment if(IsPathFragmentString(pStruct40->NodeIndex)) { DWORD FragmentOffset = GetPathFragmentOffset2(HiBitsIndex, pStruct40->NodeIndex); DWORD SavePathLength = pStruct40->PathLength; // HOTS: 1957A83 // Do we have a child database? if(pChildDB != NULL) { // HOTS: 1957AEC if(pChildDB->ComparePathFragmentByIndex(pSearch, FragmentOffset)) return true; } else { // HOTS: 1957AF7 if(PathFragmentTable.ComparePathFragment(pSearch, FragmentOffset)) return true; } // HOTS: 1957B0E // If there was partial match with the fragment, end the search if(pStruct40->PathLength != SavePathLength) return false; } else { // HOTS: 1957B1C if(LoBitsTable[pStruct40->NodeIndex] == pSearch->szSearchMask[pStruct40->PathLength]) { pStruct40->PathLength++; return true; } } // HOTS: 1957B32 pStruct40->NodeIndex++; ColTableIndex++; } return false; } // HOTS: 1957B80 bool ComparePathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex) { TStruct40 * pStruct40 = &pSearch->Struct40; PHASH_ENTRY pHashEntry; DWORD eax; // HOTS: 1957B95 for (;;) { // Get the hasn table item pHashEntry = &HashTable[TableIndex & HashTableMask]; // if (TableIndex == pHashEntry->NextIndex) { // HOTS: 01957BB4 if (!IsPathFragmentSingleChar(pHashEntry)) { // HOTS: 1957BC7 if (pChildDB != NULL) { // HOTS: 1957BD3 if (!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) return false; } else { // HOTS: 1957BE0 if (!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset)) return false; } } else { // HOTS: 1957BEE if (pSearch->szSearchMask[pStruct40->PathLength] != pHashEntry->SingleChar) return false; pStruct40->PathLength++; } // HOTS: 1957C05 TableIndex = pHashEntry->NodeIndex; if (TableIndex == 0) return true; if (pStruct40->PathLength >= pSearch->cchSearchMask) return false; } else { // HOTS: 1957C30 if (IsPathFragmentString(TableIndex)) { DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex); // HOTS: 1957C4C if (pChildDB != NULL) { // HOTS: 1957C58 if (!pChildDB->ComparePathFragmentByIndex(pSearch, FragmentOffset)) return false; } else { // HOTS: 1957350 if (!PathFragmentTable.ComparePathFragment(pSearch, FragmentOffset)) return false; } } else { // HOTS: 1957C8E if (LoBitsTable[TableIndex] != pSearch->szSearchMask[pStruct40->PathLength]) return false; pStruct40->PathLength++; } // HOTS: 1957CB2 if (TableIndex <= field_214) return true; if (pStruct40->PathLength >= pSearch->cchSearchMask) return false; eax = CollisionTable.GetItem1(TableIndex); TableIndex = (eax - TableIndex - 1); } } } // HOTS: 1958D70 void CopyPathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex) { TStruct40 * pStruct40 = &pSearch->Struct40; PHASH_ENTRY pHashEntry; // HOTS: 1958D84 for (;;) { pHashEntry = &HashTable[TableIndex & HashTableMask]; if (TableIndex == pHashEntry->NextIndex) { // HOTS: 1958DA6 if (!IsPathFragmentSingleChar(pHashEntry)) { // HOTS: 1958DBA if (pChildDB != NULL) { pChildDB->CopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex); } else { PathFragmentTable.CopyPathFragment(pSearch, pHashEntry->FragmentOffset); } } else { // HOTS: 1958DE7 // Insert the low 8 bits to the path being built pStruct40->PathBuffer.Insert(pHashEntry->SingleChar); } // HOTS: 1958E71 TableIndex = pHashEntry->NodeIndex; if (TableIndex == 0) return; } else { // HOTS: 1958E8E if (IsPathFragmentString(TableIndex)) { DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex); // HOTS: 1958EAF if (pChildDB != NULL) { pChildDB->CopyPathFragmentByIndex(pSearch, FragmentOffset); } else { PathFragmentTable.CopyPathFragment(pSearch, FragmentOffset); } } else { // HOTS: 1958F50 // Insert one character to the path being built pStruct40->PathBuffer.Insert(LoBitsTable[TableIndex]); } // HOTS: 1958FDE if (TableIndex <= field_214) return; TableIndex = 0xFFFFFFFF - TableIndex + CollisionTable.GetItem1(TableIndex); } } } // HOTS: 1958B00 bool CompareAndCopyPathFragment(TMndxSearch * pSearch) { TStruct40 * pStruct40 = &pSearch->Struct40; PHASH_ENTRY pHashEntry; DWORD HiBitsIndex; DWORD ColTableIndex; DWORD TableIndex; /* FILE * fp = fopen("E:\\PathFragmentTable.txt", "wt"); if (fp != NULL) { for (DWORD i = 0; i < HashTable.ItemCount; i++) { FragOffs = HashTable[i].FragOffs; fprintf(fp, "%02x ('%c') %08X %08X %08X", i, (0x20 <= i && i < 0x80) ? i : 0x20, HashTable[i].ItemIndex, HashTable[i].NextIndex, FragOffs); if(FragOffs != 0x00800000) { if((FragOffs & 0xFFFFFF00) == 0xFFFFFF00) fprintf(fp, " '%c'", (char)(FragOffs & 0xFF)); else fprintf(fp, " %s", &PathFragmentTable.PathFragments[FragOffs]); } fprintf(fp, "\n"); } fclose(fp); } */ // Calculate the item hash from the current char and fragment ID TableIndex = pStruct40->CalcHashValue(pSearch->szSearchMask) & HashTableMask; pHashEntry = &HashTable[TableIndex]; // Does the hash value ID match? if(pStruct40->NodeIndex == pHashEntry->NodeIndex) { // If the higher 24 bits are set, then the fragment is just one letter, // contained directly in the table. if(!IsPathFragmentSingleChar(pHashEntry)) { // HOTS: 1958B59 if (pChildDB != NULL) { if (!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) return false; } else { if (!PathFragmentTable.CompareAndCopyPathFragment(pSearch, pHashEntry->FragmentOffset)) return false; } } else { // HOTS: 1958B88 pStruct40->PathBuffer.Insert(pHashEntry->SingleChar); pStruct40->PathLength++; } // HOTS: 1958BCA pStruct40->NodeIndex = pHashEntry->NextIndex; return true; } // HOTS: 1958BE5 ColTableIndex = CollisionTable.GetItem0(pStruct40->NodeIndex) + 1; pStruct40->NodeIndex = (ColTableIndex - pStruct40->NodeIndex - 1); HiBitsIndex = CASC_INVALID_INDEX; // Keep searching while we have a valid collision table entry while(CollisionTable.IsItemPresent(ColTableIndex)) { // If we have high bits in the the bit at NodeIndex is set, it means that there is fragment offset // If not, the byte in LoBitsTable is the character if(IsPathFragmentString(pStruct40->NodeIndex)) { DWORD FragmentOffset = GetPathFragmentOffset2(HiBitsIndex, pStruct40->NodeIndex); DWORD SavePathLength = pStruct40->PathLength; // HOTS: 1958C62 // Do we have a child database? if(pChildDB != NULL) { // HOTS: 1958CCB if(pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, FragmentOffset)) return true; } else { // HOTS: 1958CD6 if(PathFragmentTable.CompareAndCopyPathFragment(pSearch, FragmentOffset)) return true; } // HOTS: 1958CED if(SavePathLength != pStruct40->PathLength) return false; } else { // HOTS: 1958CFB if(LoBitsTable[pStruct40->NodeIndex] == pSearch->szSearchMask[pStruct40->PathLength]) { // HOTS: 1958D11 pStruct40->PathBuffer.Insert(LoBitsTable[pStruct40->NodeIndex]); pStruct40->PathLength++; return true; } } // HOTS: 1958D11 pStruct40->NodeIndex++; ColTableIndex++; } return false; } // HOTS: 1959010 bool CompareAndCopyPathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex) { TStruct40 * pStruct40 = &pSearch->Struct40; PHASH_ENTRY pHashEntry; // HOTS: 1959024 for(;;) { pHashEntry = &HashTable[TableIndex & HashTableMask]; if(TableIndex == pHashEntry->NextIndex) { // HOTS: 1959047 if(!IsPathFragmentSingleChar(pHashEntry)) { // HOTS: 195905A if(pChildDB != NULL) { if(!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) return false; } else { if(!PathFragmentTable.CompareAndCopyPathFragment(pSearch, pHashEntry->FragmentOffset)) return false; } } else { // HOTS: 1959092 if(pHashEntry->SingleChar != pSearch->szSearchMask[pStruct40->PathLength]) return false; // Insert the low 8 bits to the path being built pStruct40->PathBuffer.Insert(pHashEntry->SingleChar); pStruct40->PathLength++; } // HOTS: 195912E TableIndex = pHashEntry->NodeIndex; if(TableIndex == 0) return true; } else { // HOTS: 1959147 if(IsPathFragmentString(TableIndex)) { // HOTS: 195917C DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex); if(pChildDB != NULL) { if(!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, FragmentOffset)) return false; } else { if(!PathFragmentTable.CompareAndCopyPathFragment(pSearch, FragmentOffset)) return false; } } else { // HOTS: 195920E if(LoBitsTable[TableIndex] != pSearch->szSearchMask[pStruct40->PathLength]) return false; // Insert one character to the path being built pStruct40->PathBuffer.Insert(LoBitsTable[TableIndex]); pStruct40->PathLength++; } // HOTS: 19592B6 if(TableIndex <= field_214) return true; TableIndex = 0xFFFFFFFF - TableIndex + CollisionTable.GetItem1(TableIndex); } // HOTS: 19592D5 if(pStruct40->PathLength >= pSearch->cchSearchMask) break; } CopyPathFragmentByIndex(pSearch, TableIndex); return true; } // HOTS: 1959460 bool DoSearch(TMndxSearch * pSearch) { TStruct40 * pStruct40 = &pSearch->Struct40; TPathStop * pPathStop; DWORD edi; // Perform action based on the search phase switch (pStruct40->SearchPhase) { case MNDX_SEARCH_INITIALIZING: { // HOTS: 1959489 pStruct40->BeginSearch(); // If the caller passed a part of the search path, we need to find that one while (pStruct40->PathLength < pSearch->cchSearchMask) { if (!CompareAndCopyPathFragment(pSearch)) { pStruct40->SearchPhase = MNDX_SEARCH_FINISHED; return false; } } // HOTS: 19594b0 TPathStop PathStop(pStruct40->NodeIndex, 0, pStruct40->PathBuffer.ItemCount); pStruct40->PathStops.Insert(PathStop); pStruct40->ItemCount = 1; if (FileNameIndexes.IsItemPresent(pStruct40->NodeIndex)) { pSearch->szFoundPath = &pStruct40->PathBuffer[0]; pSearch->cchFoundPath = pStruct40->PathBuffer.ItemCount; pSearch->nIndex = FileNameIndexes.GetItemValueAt(pStruct40->NodeIndex); return true; } } // No break here, go straight to the MNDX_SEARCH_SEARCHING case MNDX_SEARCH_SEARCHING: { // HOTS: 1959522 for (;;) { // HOTS: 1959530 if (pStruct40->ItemCount == pStruct40->PathStops.ItemCount) { TPathStop * pLastStop; DWORD ColTableIndex; pLastStop = &pStruct40->PathStops[pStruct40->PathStops.ItemCount - 1]; ColTableIndex = CollisionTable.GetItem0(pLastStop->LoBitsIndex) + 1; // Insert a new structure TPathStop PathStop(ColTableIndex - pLastStop->LoBitsIndex - 1, ColTableIndex, 0); pStruct40->PathStops.Insert(PathStop); } // HOTS: 19595BD pPathStop = &pStruct40->PathStops[pStruct40->ItemCount]; // HOTS: 19595CC if (CollisionTable.IsItemPresent(pPathStop->field_4++)) { // HOTS: 19595F2 pStruct40->ItemCount++; if (IsPathFragmentString(pPathStop->LoBitsIndex)) { DWORD FragmentOffset = GetPathFragmentOffset2(pPathStop->HiBitsIndex_PathFragment, pPathStop->LoBitsIndex); // HOTS: 1959630 if (pChildDB != NULL) { // HOTS: 1959649 pChildDB->CopyPathFragmentByIndex(pSearch, FragmentOffset); } else { // HOTS: 1959654 PathFragmentTable.CopyPathFragment(pSearch, FragmentOffset); } } else { // HOTS: 1959665 // Insert one character to the path being built pStruct40->PathBuffer.Insert(LoBitsTable[pPathStop->LoBitsIndex]); } // HOTS: 19596AE pPathStop->Count = pStruct40->PathBuffer.ItemCount; // HOTS: 19596b6 if (FileNameIndexes.IsItemPresent(pPathStop->LoBitsIndex)) { // HOTS: 19596D1 if (pPathStop->field_10 == 0xFFFFFFFF) { // HOTS: 19596D9 pPathStop->field_10 = FileNameIndexes.GetItemValueAt(pPathStop->LoBitsIndex); } else { pPathStop->field_10++; } // HOTS: 1959755 pSearch->szFoundPath = &pStruct40->PathBuffer[0]; pSearch->cchFoundPath = pStruct40->PathBuffer.ItemCount; pSearch->nIndex = pPathStop->field_10; return true; } } else { // HOTS: 19596E9 if (pStruct40->ItemCount == 1) { pStruct40->SearchPhase = MNDX_SEARCH_FINISHED; return false; } // HOTS: 19596F5 pStruct40->PathStops[pStruct40->ItemCount - 1].LoBitsIndex++; edi = pStruct40->PathStops[pStruct40->ItemCount - 2].Count; pStruct40->PathBuffer.SetMaxItemsIf(edi); // HOTS: 1959749 pStruct40->PathBuffer.ItemCount = edi; pStruct40->ItemCount--; } } } case MNDX_SEARCH_FINISHED: break; } return false; } // HOTS: 1957EF0 bool FindFileInDatabase(TMndxSearch * pSearch) { TStruct40 * pStruct40 = &pSearch->Struct40; pStruct40->NodeIndex = 0; pStruct40->PathLength = 0; pStruct40->SearchPhase = MNDX_SEARCH_INITIALIZING; if(pSearch->cchSearchMask > 0) { while(pStruct40->PathLength < pSearch->cchSearchMask) { // HOTS: 01957F12 if(!ComparePathFragment(pSearch)) return false; } } // HOTS: 1957F26 if(!FileNameIndexes.IsItemPresent(pStruct40->NodeIndex)) return false; pSearch->szFoundPath = pSearch->szSearchMask; pSearch->cchFoundPath = pSearch->cchSearchMask; pSearch->nIndex = FileNameIndexes.GetItemValueAt(pStruct40->NodeIndex); return true; } // HOTS: 1959790 DWORD LoadFromStream(TByteStream & InStream) { DWORD dwBitMask; DWORD dwErrCode; dwErrCode = CollisionTable.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = FileNameIndexes.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = CollisionHiBitsIndexes.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // HOTS: 019597CD dwErrCode = LoBitsTable.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = HiBitsTable.LoadBitsFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // HOTS: 019597F5 dwErrCode = PathFragmentTable.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // HOTS: 0195980A if(CollisionHiBitsIndexes.ValidItemCount != 0 && PathFragmentTable.PathFragments.ItemCount == 0) { TFileNameDatabase * pNewDB; pNewDB = new TFileNameDatabase; if (pNewDB == NULL) return ERROR_NOT_ENOUGH_MEMORY; dwErrCode = SetChildDatabase(pNewDB); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = pChildDB->LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; } // HOTS: 0195986B dwErrCode = HashTable.LoadFromStream(InStream); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; HashTableMask = HashTable.ItemCount - 1; dwErrCode = InStream.GetValue(field_214); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; dwErrCode = InStream.GetValue(dwBitMask); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; return Struct10.sub_1957800(dwBitMask); } TSparseArray CollisionTable; // Table of valid collisions, indexed by NodeIndex TSparseArray FileNameIndexes; // Array of file name indexes TSparseArray CollisionHiBitsIndexes; // Table of indexes of high bits (above 8 bits) for collisions // This pair of arrays serves for fast conversion from node index to FragmentOffset / FragmentChar TGenericArray LoBitsTable; // Array of lower 8 bits of name fragment offset TBitEntryArray HiBitsTable; // Array of upper x bits of name fragment offset TPathFragmentTable PathFragmentTable; TFileNameDatabase * pChildDB; TGenericArray HashTable; // Hash table for searching name fragments DWORD HashTableMask; // Mask to get hash table index from hash value DWORD field_214; TStruct10 Struct10; }; //----------------------------------------------------------------------------- // Local functions - MAR file class TMndxMarFile { public: TMndxMarFile() { pDatabase = NULL; pbMarData = NULL; cbMarData = 0; } ~TMndxMarFile() { if(pDatabase != NULL) delete pDatabase; CASC_FREE(pbMarData); } // HOTS: 00E94180 int LoadRootData(FILE_MAR_INFO & MarInfo, LPBYTE pbRootFile, LPBYTE pbRootEnd) { // Allocate the MAR data pbMarData = CASC_ALLOC(MarInfo.MarDataSize); cbMarData = MarInfo.MarDataSize; if(pbMarData == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Capture the MAR data if(!CaptureData(pbRootFile + MarInfo.MarDataOffset, pbRootEnd, pbMarData, cbMarData)) return ERROR_FILE_CORRUPT; // Create the file name database pDatabase = new TFileNameDatabase(); if(pDatabase == NULL) return ERROR_NOT_ENOUGH_MEMORY; return pDatabase->Load(pbMarData, cbMarData); } // HOTS: 1956C60 DWORD SearchFile(TMndxSearch * pSearch) { DWORD dwErrCode = ERROR_SUCCESS; if(pDatabase == NULL) return ERROR_INVALID_PARAMETER; if(!pDatabase->FindFileInDatabase(pSearch)) dwErrCode = ERROR_FILE_NOT_FOUND; return dwErrCode; } // HOTS: 1956CE0 DWORD DoSearch(TMndxSearch * pSearch, bool * pbFindResult) { DWORD dwErrCode = ERROR_SUCCESS; if(pDatabase == NULL) return ERROR_INVALID_PARAMETER; *pbFindResult = pDatabase->DoSearch(pSearch); return dwErrCode; } // HOTS: 1956D20 int GetFileNameCount(size_t * PtrFileNameCount) { if(pDatabase == NULL) return ERROR_INVALID_PARAMETER; PtrFileNameCount[0] = pDatabase->FileNameIndexes.ValidItemCount; return ERROR_SUCCESS; } // protected: TFileNameDatabase * pDatabase; LPBYTE pbMarData; size_t cbMarData; }; //----------------------------------------------------------------------------- // Implementation of root file functions typedef struct _FILE_MNDX_INFO { BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file DWORD HeaderVersion; // Must be <= 2 DWORD FormatVersion; DWORD field_1C; DWORD field_20; DWORD MarInfoOffset; // Offset of the first MAR entry info DWORD MarInfoCount; // Number of the MAR info entries DWORD MarInfoSize; // Size of the MAR info entry DWORD CKeyEntriesOffset; // Offset of the CKey entries, relative to begin of the root file DWORD CKeyEntriesCount; // Number of CKeys (files) in the root file DWORD FileNameCount; // Number of unique file names. More files with the same name in the different packages can exist DWORD CKeyEntrySize; // Size of one CKey root entry TMndxMarFile * MarFiles[MAR_COUNT]; // File name list for the packages } FILE_MNDX_INFO, *PFILE_MNDX_INFO; struct TMndxHandler { public: // // Constructor and destructor // TMndxHandler() { memset(this, 0, sizeof(TMndxHandler)); } ~TMndxHandler() { PMNDX_PACKAGE pPackage; size_t i; for(i = 0; i < MAR_COUNT; i++) delete MndxInfo.MarFiles[i]; CASC_FREE(FileNameIndexToCKeyIndex); pCKeyEntries = NULL; for(i = 0; i < Packages.ItemCount(); i++) { pPackage = (PMNDX_PACKAGE)Packages.ItemAt(i); CASC_FREE(pPackage->szFileName); } Packages.Free(); } // // Helper functions // static LPBYTE CaptureRootHeader(FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootPtr, LPBYTE pbRootEnd) { // Capture the root header pbRootPtr = CaptureData(pbRootPtr, pbRootEnd, &MndxHeader, sizeof(FILE_MNDX_HEADER)); if (pbRootPtr == NULL) return NULL; // Check signature and version if (MndxHeader.Signature != CASC_MNDX_ROOT_SIGNATURE || MndxHeader.FormatVersion > 2 || MndxHeader.FormatVersion < 1) return NULL; // Passed return pbRootPtr + sizeof(FILE_MNDX_HEADER); } DWORD LoadPackageNames() { TMndxMarFile * pMarFile = MndxInfo.MarFiles[MAR_PACKAGE_NAMES]; TMndxSearch Search; PMNDX_PACKAGE pPackage; size_t nPackageCount = 0x40; bool bFindResult = false; DWORD dwErrCode; // Prepare the file name search in the top level directory Search.SetSearchMask("", 0); // Allocate initial name list structure pMarFile->GetFileNameCount(&nPackageCount); dwErrCode = Packages.Create(nPackageCount); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Reset the package array Packages.Reset(); // Keep searching as long as we find something while(pMarFile->DoSearch(&Search, &bFindResult) == ERROR_SUCCESS && bFindResult) { // Insert new package to the array assert(Search.nIndex < nPackageCount); pPackage = (PMNDX_PACKAGE)Packages.InsertAt(Search.nIndex); if (pPackage != NULL) { // The package mut not be initialized yet assert(pPackage->szFileName == NULL); // Allocate space for the file name pPackage->szFileName = CASC_ALLOC(Search.cchFoundPath + 1); if (pPackage->szFileName == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Fill the package structure memcpy(pPackage->szFileName, Search.szFoundPath, Search.cchFoundPath); pPackage->szFileName[Search.cchFoundPath] = 0; pPackage->nLength = Search.cchFoundPath; pPackage->nIndex = Search.nIndex; } } // Give the packages to the caller return ERROR_SUCCESS; } DWORD Load(const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd) { TMndxMarFile * pMarFile; FILE_MAR_INFO MarInfo; size_t nFilePointer = 0; DWORD i; DWORD dwErrCode = ERROR_SUCCESS; // Copy the header into the MNDX info MndxInfo.HeaderVersion = MndxHeader.HeaderVersion; MndxInfo.FormatVersion = MndxHeader.FormatVersion; nFilePointer += sizeof(FILE_MNDX_HEADER); // Header version 2 has 2 extra fields that we need to load if(MndxInfo.HeaderVersion == 2) { if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MndxInfo.field_1C, sizeof(DWORD) + sizeof(DWORD))) return ERROR_FILE_CORRUPT; nFilePointer += sizeof(DWORD) + sizeof(DWORD); } // Load the rest of the file header if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MndxInfo.MarInfoOffset, 0x1C)) return ERROR_FILE_CORRUPT; // Verify the structure if(MndxInfo.MarInfoCount > MAR_COUNT || MndxInfo.MarInfoSize != sizeof(FILE_MAR_INFO)) return ERROR_FILE_CORRUPT; // Load all MAR infos for(i = 0; i < MndxInfo.MarInfoCount; i++) { // Capture the n-th MAR info nFilePointer = MndxInfo.MarInfoOffset + (MndxInfo.MarInfoSize * i); if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MarInfo, sizeof(FILE_MAR_INFO))) return ERROR_FILE_CORRUPT; // Allocate MAR_FILE structure pMarFile = new TMndxMarFile(); if(pMarFile == NULL) { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; break; } // Create the database from the MAR data dwErrCode = pMarFile->LoadRootData(MarInfo, pbRootFile, pbRootEnd); if(dwErrCode != ERROR_SUCCESS) break; // Assign the MAR file to the MNDX info structure MndxInfo.MarFiles[i] = pMarFile; } // All three MAR files must be loaded // HOTS: 00E9503B if(dwErrCode == ERROR_SUCCESS) { if(MndxInfo.MarFiles[MAR_PACKAGE_NAMES] == NULL || MndxInfo.MarFiles[MAR_STRIPPED_NAMES] == NULL || MndxInfo.MarFiles[MAR_FULL_NAMES] == NULL) dwErrCode = ERROR_BAD_FORMAT; if(MndxInfo.CKeyEntrySize != sizeof(MNDX_CKEY_ENTRY)) dwErrCode = ERROR_BAD_FORMAT; } // Load the array of Ckey entries. All present files are in the array, // the same names (differentiated by package ID) are groupped together if(dwErrCode == ERROR_SUCCESS) { size_t CKeyEntriesSize; size_t FileNameCount = 0; pMarFile = MndxInfo.MarFiles[MAR_STRIPPED_NAMES]; dwErrCode = ERROR_FILE_CORRUPT; // Capture the array of CKey entries if(pMarFile->GetFileNameCount(&FileNameCount) == ERROR_SUCCESS && FileNameCount == MndxInfo.FileNameCount) { CKeyEntriesSize = MndxInfo.CKeyEntriesCount * MndxInfo.CKeyEntrySize; if ((pbRootFile + MndxInfo.CKeyEntriesOffset + CKeyEntriesSize) <= pbRootEnd) { pCKeyEntries = (PMNDX_CKEY_ENTRY)(pbRootFile + MndxInfo.CKeyEntriesOffset); dwErrCode = ERROR_SUCCESS; } } } // Pick the CKey entries that are the first with a given name if(dwErrCode == ERROR_SUCCESS) { assert(MndxInfo.FileNameCount <= MndxInfo.CKeyEntriesCount); FileNameIndexToCKeyIndex = CASC_ALLOC(MndxInfo.FileNameCount + 1); if(FileNameIndexToCKeyIndex != NULL) { PMNDX_CKEY_ENTRY pRootEntry = pCKeyEntries; DWORD nFileNameIndex = 0; // The first entry is always beginning of a file name group FileNameIndexToCKeyIndex[nFileNameIndex++] = pRootEntry; // Get the remaining file name groups for(i = 0; i < MndxInfo.CKeyEntriesCount; i++, pRootEntry++) { if (nFileNameIndex > MndxInfo.FileNameCount) break; if (pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY) { FileNameIndexToCKeyIndex[nFileNameIndex++] = pRootEntry + 1; } } // Verify the final number of file names if ((nFileNameIndex - 1) != MndxInfo.FileNameCount) dwErrCode = ERROR_BAD_FORMAT; } else dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Load the package names from the 0-th MAR file if(dwErrCode == ERROR_SUCCESS) { dwErrCode = LoadPackageNames(); } return dwErrCode; } DWORD LoadFileNames(TCascStorage * hs, CASC_FILE_TREE & FileTree) { PCASC_CKEY_ENTRY pCKeyEntry; PMNDX_CKEY_ENTRY pRootEntry; PMNDX_CKEY_ENTRY pRootEnd = pCKeyEntries + MndxInfo.CKeyEntriesCount; PMNDX_PACKAGE pPackage; TMndxMarFile * pMarFile = MndxInfo.MarFiles[MAR_STRIPPED_NAMES]; TMndxSearch Search; char szFileName[MAX_PATH]; bool bFindResult = false; DWORD dwErrCode; // Setup the search mask Search.SetSearchMask("", 0); // Keep searching ad long as we found something while ((dwErrCode = pMarFile->DoSearch(&Search, &bFindResult)) == ERROR_SUCCESS && bFindResult) { // Sanity check assert(Search.cchFoundPath < MAX_PATH); // The found file name index must fall into range of file names if (Search.nIndex < MndxInfo.FileNameCount) { // Retrieve the first-in-group CKey entry of that name pRootEntry = FileNameIndexToCKeyIndex[Search.nIndex]; // Now take all files of that name, prepend their package name and insert to file tree while(pRootEntry < pRootEnd) { // Find the appropriate CKey entry in the central storage pCKeyEntry = FindCKeyEntry_CKey(hs, pRootEntry->CKey); if (pCKeyEntry != NULL) { size_t nPackageIndex = pRootEntry->Flags & 0x00FFFFFF; // Retrieve the package for this entry pPackage = (PMNDX_PACKAGE)Packages.ItemAt(nPackageIndex); if (pPackage != NULL) { // Sanity check assert(pPackage->nIndex == nPackageIndex); // Merge the package name and file name MakeFileName(szFileName, _countof(szFileName), pPackage, &Search); // Insert the entry to the file tree FileTree.InsertByName(pCKeyEntry, szFileName); } } // Is this the last-in-group entry? if (pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY) break; pRootEntry++; } } } return dwErrCode; } // // Helper functions // void MakeFileName(char * szBuffer, size_t cchBuffer, PMNDX_PACKAGE pPackage, TMndxSearch * pSearch) { char * szBufferEnd = szBuffer + cchBuffer - 1; // Buffer length check assert((pPackage->nLength + 1 + pSearch->cchFoundPath + 1) < cchBuffer); // Copy the package name if ((szBuffer + pPackage->nLength) < szBufferEnd) { memcpy(szBuffer, pPackage->szFileName, pPackage->nLength); szBuffer += pPackage->nLength; } // Append slash if ((szBuffer + 1) < szBufferEnd) *szBuffer++ = '/'; // Append file name if ((szBuffer + pSearch->cchFoundPath) < szBufferEnd) { memcpy(szBuffer, pSearch->szFoundPath, pSearch->cchFoundPath); szBuffer += pSearch->cchFoundPath; } szBuffer[0] = 0; } protected: FILE_MNDX_INFO MndxInfo; PMNDX_CKEY_ENTRY * FileNameIndexToCKeyIndex; PMNDX_CKEY_ENTRY pCKeyEntries; CASC_ARRAY Packages; // Linear list of present packages }; //----------------------------------------------------------------------------- // Handler definition for MNDX root file struct TRootHandler_MNDX : public TFileTreeRoot { public: TRootHandler_MNDX() : TFileTreeRoot(0) { // MNDX supports file names and CKeys dwFeatures |= CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY; } DWORD Load(TCascStorage * hs, const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd) { TMndxHandler Handler; DWORD dwErrCode; // Load and parse the entire MNDX structure dwErrCode = Handler.Load(MndxHeader, pbRootFile, pbRootEnd); if (dwErrCode == ERROR_SUCCESS) { // Search all file names and insert them into the file tree dwErrCode = Handler.LoadFileNames(hs, FileTree); } return dwErrCode; } }; //----------------------------------------------------------------------------- // Public functions - MNDX info DWORD RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) { TRootHandler_MNDX * pRootHandler = NULL; FILE_MNDX_HEADER MndxHeader; LPBYTE pbRootEnd = pbRootFile + cbRootFile; DWORD dwErrCode = ERROR_BAD_FORMAT; // Verify the header of the ROOT file if(TMndxHandler::CaptureRootHeader(MndxHeader, pbRootFile, pbRootEnd) != NULL) { // Allocate the root handler object pRootHandler = new TRootHandler_MNDX(); if(pRootHandler != NULL) { // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, MndxHeader, pbRootFile, pbRootEnd); if(dwErrCode != ERROR_SUCCESS) { delete pRootHandler; pRootHandler = NULL; } } } // Assign the root directory (or NULL) and return error hs->pRootHandler = pRootHandler; return dwErrCode; } ================================================ FILE: deps/CascLib/src/CascRootFile_OW.cpp ================================================ /*****************************************************************************/ /* CascRootFile_Text.cpp Copyright (c) Ladislav Zezula 2017 */ /*---------------------------------------------------------------------------*/ /* Support for loading ROOT files in plain text */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 28.10.15 1.00 Lad The first version of CascRootFile_Text.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Structure definitions for CMF files #define MAX_LINE_ELEMENTS 8 typedef struct _CMF_HEADER_V3 { DWORD BuildVersion; DWORD Unknown0; DWORD Unknown1; DWORD Unknown2; DWORD Unknown3; DWORD DataCount; DWORD Unknown4; DWORD EntryCount; DWORD Magic; } CMF_HEADER_V3, *PCMF_HEADER_V3; typedef struct _CMF_HEADER_V2 { DWORD BuildVersion; DWORD Unknown0; DWORD Unknown1; DWORD Unknown2; DWORD DataCount; DWORD Unknown3; DWORD EntryCount; DWORD Magic; } CMF_HEADER_V2, *PCMF_HEADER_V2; typedef struct _CMF_HEADER_V1 { DWORD BuildVersion; DWORD Unknown0; DWORD DataCount; DWORD Unknown1; DWORD EntryCount; DWORD Magic; } CMF_HEADER_V1, *PCMF_HEADER_V1; //----------------------------------------------------------------------------- // Structure definitions for APM files // In-memory format typedef struct _APM_ENTRY { DWORD Index; ULONGLONG HashA; ULONGLONG HashB; } APM_ENTRY, *PAPM_ENTRY; // On-disk format, size = 0x14 typedef struct _APM_ENTRY_V2 { DWORD Index; DWORD HashA_Lo; // Must split the hashes in order to make this structure properly aligned DWORD HashA_Hi; DWORD HashB_Lo; DWORD HashB_Hi; } APM_ENTRY_V2, *PAPM_ENTRY_V2; // On-disk format, size = 0x0C typedef struct _APM_ENTRY_V1 { DWORD Index; DWORD HashA_Lo; // Must split the hashes in order to make this structure properly aligned DWORD HashA_Hi; } APM_ENTRY_V1, *PAPM_ENTRY_V1; // In-memory format typedef struct _APM_PACKAGE_ENTRY { ULONGLONG PackageGUID; // 077 file ULONGLONG Unknown1; DWORD Unknown2; DWORD Unknown3; ULONGLONG Unknown4; } APM_PACKAGE_ENTRY, *PAPM_PACKAGE_ENTRY; // On-disk format typedef struct _APM_PACKAGE_ENTRY_V2 { ULONGLONG PackageGUID; // 077 file ULONGLONG Unknown1; DWORD Unknown2; DWORD Unknown3; ULONGLONG Unknown4; } APM_PACKAGE_ENTRY_V2, *PAPM_PACKAGE_ENTRY_V2; // On-disk format typedef struct _APM_PACKAGE_ENTRY_V1 { ULONGLONG EntryPointGUID; // virtual most likely ULONGLONG PrimaryGUID; // real ULONGLONG SecondaryGUID; // real ULONGLONG Key; // encryption ULONGLONG PackageGUID; // 077 file ULONGLONG Unknown1; DWORD Unknown2; } APM_PACKAGE_ENTRY_V1, *PAPM_PACKAGE_ENTRY_V1; typedef struct _APM_HEADER_V3 { ULONGLONG BuildNumber; // Build number of the game ULONGLONG ZeroValue1; DWORD ZeroValue2; DWORD PackageCount; DWORD ZeroValue3; DWORD EntryCount; DWORD Checksum; // Followed by the array of APM_ENTRY (count is in "EntryCount") // Followed by the array of APM_PACKAGE (count is in "PackageCount") } APM_HEADER_V3, *PAPM_HEADER_V3; typedef struct _APM_HEADER_V2 { ULONGLONG BuildNumber; // Build number of the game ULONGLONG ZeroValue1; DWORD PackageCount; DWORD ZeroValue2; DWORD EntryCount; DWORD Checksum; // Followed by the array of APM_ENTRY (count is in "EntryCount") // Followed by the array of APM_PACKAGE (count is in "PackageCount") } APM_HEADER_V2, *PAPM_HEADER_V2; typedef struct _APM_HEADER_V1 { ULONGLONG BuildNumber; // Build number of the game DWORD BuildVersion; DWORD PackageCount; DWORD EntryCount; DWORD Checksum; // Followed by the array of APM_ENTRY (count is in "EntryCount") // Followed by the array of APM_PACKAGE (count is in "PackageCount") } APM_HEADER_V1, *PAPM_HEADER_V1; //----------------------------------------------------------------------------- // Handler classes /* struct TCmfFile { TCmfFile() { memset(this, 0, sizeof(TCmfFile)); } LPBYTE CaptureHeader(LPBYTE pbCmfData, LPBYTE pbCmfEnd) { DWORD BuildNumber = *(PDWORD)pbCmfData; // Check the newest header version if(BuildNumber >= 45104 && BuildNumber != 45214) { PCMF_HEADER_V3 pHeader3 = (PCMF_HEADER_V3)pbCmfData; if ((LPBYTE)(pHeader3 + 1) > pbCmfEnd) return NULL; BuildVersion = pHeader3->BuildVersion; DataCount = pHeader3->DataCount; EntryCount = pHeader3->EntryCount; Magic = pHeader3->Magic; return (LPBYTE)(pHeader3 + 1); } else if(BuildNumber >= 39028) { // TODO assert(false); return NULL; } else { // TODO assert(false); return NULL; } } DWORD BuildVersion; DWORD DataCount; DWORD EntryCount; DWORD Magic; }; struct TApmFile { TApmFile() { memset(this, 0, sizeof(TApmFile)); } ~TApmFile() { CASC_FREE(pApmPackages); CASC_FREE(pApmEntries); } LPBYTE CaptureHeader(LPBYTE pbApmData, LPBYTE pbApmEnd) { // Check the data size for the largest possible header size if((pbApmData + sizeof(APM_HEADER_V3)) < pbApmEnd) { // Try the version 3 PAPM_HEADER_V3 pApmFile3 = (PAPM_HEADER_V3)(pbApmData); if(pApmFile3->ZeroValue1 == 0 && pApmFile3->ZeroValue2 == 0 && pApmFile3->PackageCount && pApmFile3->EntryCount && pApmFile3->Checksum) { BuildNumber = pApmFile3->BuildNumber; PackageCount = pApmFile3->PackageCount; EntryCount = pApmFile3->EntryCount; Checksum = pApmFile3->Checksum; return pbApmData + 0x24; } // Try the version 2 PAPM_HEADER_V2 pApmFile2 = (PAPM_HEADER_V2)(pbApmData); if(pApmFile2->ZeroValue1 == 0 && pApmFile2->PackageCount && pApmFile2->EntryCount && pApmFile2->Checksum) { BuildNumber = pApmFile2->BuildNumber; PackageCount = pApmFile2->PackageCount; EntryCount = pApmFile2->EntryCount; Checksum = pApmFile2->Checksum; return pbApmData + 0x20; } // Try the version 1 (build 24919) PAPM_HEADER_V1 pApmHeader1 = (PAPM_HEADER_V1)(pbApmData); if(pApmHeader1->BuildVersion != 0 && pApmHeader1->PackageCount && pApmHeader1->EntryCount && pApmHeader1->Checksum) { BuildNumber = pApmHeader1->BuildNumber; PackageCount = pApmHeader1->PackageCount; EntryCount = pApmHeader1->EntryCount; Checksum = pApmHeader1->Checksum; return pbApmData + 0x18; } } return NULL; } LPBYTE CaptureArrayOfEntries(LPBYTE pbArrayOfEntries, LPBYTE pbApmEnd) { // Allocate array of entries pApmEntries = CASC_ALLOC(EntryCount); if(pApmEntries != NULL) { // The newest format if(BuildNumber > 45104 && BuildNumber != 45214) { PAPM_ENTRY_V2 pEntry2 = (PAPM_ENTRY_V2)pbArrayOfEntries; LPBYTE pbEntriesEnd = (LPBYTE)(pEntry2 + EntryCount); if(pbEntriesEnd <= pbApmEnd) { for(DWORD i = 0; i < EntryCount; i++) { pApmEntries[i].Index = pEntry2->Index; pApmEntries[i].HashA = MAKE_OFFSET64(pEntry2->HashA_Hi, pEntry2->HashA_Lo); pApmEntries[i].HashB = MAKE_OFFSET64(pEntry2->HashB_Hi, pEntry2->HashB_Lo); } return pbEntriesEnd; } } else { PAPM_ENTRY_V1 pEntry1 = (PAPM_ENTRY_V1)pbArrayOfEntries; LPBYTE pbEntriesEnd = (LPBYTE)(pEntry1 + EntryCount); if(pbEntriesEnd <= pbApmEnd) { for(DWORD i = 0; i < EntryCount; i++) { pApmEntries[i].Index = pEntry1->Index; pApmEntries[i].HashA = MAKE_OFFSET64(pEntry1->HashA_Hi, pEntry1->HashA_Lo); pApmEntries[i].HashB = 0; } return pbEntriesEnd; } } } return NULL; } LPBYTE CapturePackageEntries(LPBYTE pbArrayOfEntries, LPBYTE pbApmEnd) { // Allocate array of entries pApmPackages = CASC_ALLOC_ZERO(PackageCount); if(pApmPackages != NULL) { // The newest format if(BuildNumber > 45104 && BuildNumber != 45214) { PAPM_PACKAGE_ENTRY_V2 pEntry2 = (PAPM_PACKAGE_ENTRY_V2)pbArrayOfEntries; LPBYTE pbEntriesEnd = (LPBYTE)(pEntry2 + PackageCount); if(pbEntriesEnd <= pbApmEnd) { for(DWORD i = 0; i < PackageCount; i++) { pApmPackages[i].PackageGUID = pEntry2[i].PackageGUID; pApmPackages[i].Unknown1 = pEntry2[i].Unknown1; pApmPackages[i].Unknown2 = pEntry2[i].Unknown2; pApmPackages[i].Unknown3 = pEntry2[i].Unknown3; pApmPackages[i].Unknown4 = pEntry2[i].Unknown4; } return pbEntriesEnd; } } else { PAPM_PACKAGE_ENTRY_V1 pEntry1 = (PAPM_PACKAGE_ENTRY_V1)pbArrayOfEntries; LPBYTE pbEntriesEnd = (LPBYTE)(pEntry1 + PackageCount); if(pbEntriesEnd <= pbApmEnd) { for(DWORD i = 0; i < PackageCount; i++) { // TODO!!! pApmPackages[i].PackageGUID = pEntry1->PackageGUID; } return pbEntriesEnd; } } } return NULL; } PAPM_ENTRY pApmEntries; PAPM_PACKAGE_ENTRY pApmPackages; ULONGLONG BuildNumber; DWORD PackageCount; DWORD EntryCount; DWORD Checksum; size_t HeaderSize; // Followed by the array of APM_ENTRY (count is in "EntryCount") // Followed by the array of APM_PACKAGE (count is in "PackageCount") }; */ //----------------------------------------------------------------------------- // Handler definition for OVERWATCH root file // // ------------------------------------- // Overwatch ROOT file (build 24919): // ------------------------------------- // #MD5|CHUNK_ID|FILENAME|INSTALLPATH // FE3AD8A77EEF77B383DF4929AED816FD|0|RetailClient/GameClientApp.exe|GameClientApp.exe // 5EDDEFECA544B6472C5CD52BE63BC02F|0|RetailClient/Overwatch Launcher.exe|Overwatch Launcher.exe // 6DE09F0A67F33F874F2DD8E2AA3B7AAC|0|RetailClient/ca-bundle.crt|ca-bundle.crt // 99FE9EB6A4BB20209202F8C7884859D9|0|RetailClient/ortp_x64.dll|ortp_x64.dll // // ------------------------------------- // Overwatch ROOT file (build 47161): // ------------------------------------- // #FILEID|MD5|CHUNK_ID|PRIORITY|MPRIORITY|FILENAME|INSTALLPATH // RetailClient/Overwatch.exe|807F96661280C07E762A8C129FEBDA6F|0|0|255|RetailClient/Overwatch.exe|Overwatch.exe // RetailClient/Overwatch Launcher.exe|5EDDEFECA544B6472C5CD52BE63BC02F|0|0|255|RetailClient/Overwatch Launcher.exe|Overwatch Launcher.exe // RetailClient/ortp_x64.dll|7D1B5DEC267480F3E8DAD6B95143A59C|0|0|255|RetailClient/ortp_x64.dll|ortp_x64.dll // struct TRootHandler_OW : public TFileTreeRoot { TRootHandler_OW() : TFileTreeRoot(0) { // We have file names and return CKey as result of search dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY); } /* bool IsManifestFolderName(const char * szFileName, const char * szManifestFolder, size_t nLength) { if(!_strnicmp(szFileName, szManifestFolder, nLength)) { return (szFileName[nLength] == '\\' || szFileName[nLength] == '/'); } return false; } bool IsApmFileName(const char * szFileName) { const char * szExtension; if(IsManifestFolderName(szFileName, "Manifest", 8) || IsManifestFolderName(szFileName, "TactManifest", 12)) { szExtension = GetFileExtension(szFileName); if(!_stricmp(szExtension, ".apm")) { return true; } } return false; } DWORD LoadApmFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName) { TApmFile ApmFile; LPBYTE pbApmData; DWORD cbApmData = 0; DWORD dwErrCode = ERROR_BAD_FORMAT; pbApmData = LoadInternalFileToMemory(hs, CKey.Value, CASC_OPEN_BY_CKEY, &cbApmData); if(pbApmData != NULL) { LPBYTE pbApmEnd = pbApmData + cbApmData; LPBYTE pbApmPtr = pbApmData; pbApmPtr = ApmFile.CaptureHeader(pbApmPtr, pbApmEnd); if(pbApmPtr == NULL) return ERROR_BAD_FORMAT; // Read the array of entries pbApmPtr = ApmFile.CaptureArrayOfEntries(pbApmPtr, pbApmEnd); if(pbApmPtr == NULL) return ERROR_BAD_FORMAT; // Read the array of package entries pbApmPtr = ApmFile.CapturePackageEntries(pbApmPtr, pbApmEnd); if(pbApmPtr == NULL) return ERROR_BAD_FORMAT; CASC_FREE(pbApmData); } return dwErrCode; } static DWORD LoadCmfFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName) { TCmfFile CmfFile; LPBYTE pbCmfData; DWORD cbCmfData = 0; DWORD dwErrCode = ERROR_BAD_FORMAT; pbCmfData = LoadInternalFileToMemory(hs, CKey.Value, CASC_OPEN_BY_CKEY, &cbCmfData); if(pbCmfData != NULL) { LPBYTE pbCmfEnd = pbCmfData + cbCmfData; LPBYTE pbCmfPtr = pbCmfData; // Capture the CMF header pbCmfPtr = CmfFile.CaptureHeader(pbCmfPtr, pbCmfEnd); if(pbCmfPtr == NULL) return ERROR_BAD_FORMAT; // if(CmfFile.Magic >= 0x636D6614) // DecryptCmfFile( CASC_FREE(pbCmfData); } return dwErrCode; } */ int Load(TCascStorage * hs, CASC_CSV & Csv, size_t nFileNameIndex, size_t nCKeyIndex) { PCASC_CKEY_ENTRY pCKeyEntry; // size_t ApmFiles[0x80]; // size_t nApmFiles = 0; BYTE CKey[MD5_HASH_SIZE]; CASCLIB_UNUSED(hs); // Keep loading every line until there is something while(Csv.LoadNextLine()) { const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][nFileNameIndex]; const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][nCKeyIndex]; // Retrieve the file name and the content key if(FileName.szValue && CKeyStr.szValue && CKeyStr.nLength == MD5_STRING_SIZE) { // Convert the string CKey to binary if(BinaryFromString(CKeyStr.szValue, MD5_STRING_SIZE, CKey) == ERROR_SUCCESS) { // Find the item in the tree if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKey)) != NULL) { // Insert the file name and the CKey into the tree FileTree.InsertByName(pCKeyEntry, FileName.szValue); // If the file name is actually an asset, we need to parse that asset and load files in it // if(IsApmFileName(szFileName)) // { // ApmFiles[nApmFiles++] = FileTree_IndexOf(&pRootHandler->FileTree, pFileNode1); // } } } } } /* // Load all CMF+APM files if(dwErrCode == ERROR_SUCCESS) { for(size_t i = 0; i < nApmFiles; i++) { char szApmFile[MAX_PATH + 1]; char szCmfFile[MAX_PATH + 1]; // Get the n-th item and its name pFileNode1 = (PCASC_FILE_NODE)FileTree_PathAt(&pRootHandler->FileTree, szApmFile, MAX_PATH, ApmFiles[i]); if(pFileNode1 == NULL) break; if(strcmp(szApmFile, "TactManifest\\Win_SPWin_RDEV_LenUS_EExt.apm")) continue; // Get the name of thew CMF file CascStrCopy(szCmfFile, _countof(szCmfFile), szApmFile); CascStrCopy((char *)GetFileExtension(szCmfFile), 5, ".cmf"); pFileNode2 = (PCASC_FILE_NODE)FileTree_Find(&pRootHandler->FileTree, szCmfFile); if(pFileNode2 == NULL) break; // Create the map of CMF entries dwErrCode = LoadCmfFile(hs, pFileNode2->CKey, szCmfFile); if(dwErrCode != ERROR_SUCCESS) break; } } */ return ERROR_SUCCESS; } }; //----------------------------------------------------------------------------- // Public functions // TODO: There is way more files in the Overwatch CASC storage than present in the ROOT file. DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) { TRootHandler_OW * pRootHandler = NULL; CASC_CSV Csv(0, true); size_t Indices[2]; DWORD dwErrCode; // Load the ROOT file dwErrCode = Csv.Load(pbRootFile, cbRootFile); if(dwErrCode == ERROR_SUCCESS) { // Retrieve the indices of the file name and MD5 columns Indices[0] = Csv.GetColumnIndex("FILENAME"); Indices[1] = Csv.GetColumnIndex("MD5"); // If both indices were found OK, then load the root file if(Indices[0] != CSV_INVALID_INDEX && Indices[1] != CSV_INVALID_INDEX) { pRootHandler = new TRootHandler_OW(); if (pRootHandler != NULL) { // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, Csv, Indices[0], Indices[1]); if (dwErrCode != ERROR_SUCCESS) { delete pRootHandler; pRootHandler = NULL; } } } else { dwErrCode = ERROR_BAD_FORMAT; } } // Assign the root directory (or NULL) and return error hs->pRootHandler = pRootHandler; return dwErrCode; } ================================================ FILE: deps/CascLib/src/CascRootFile_TVFS.cpp ================================================ /*****************************************************************************/ /* CascRootFile_TVFS.cpp Copyright (c) Ladislav Zezula 2018 */ /*---------------------------------------------------------------------------*/ /* ROOT handler for TACT VFS manifest format (root) */ /* Note: TACT = Trusted Application Content Transfer */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 24.05.18 1.00 Lad The first version of CascRootFile_TVFS.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Local defines #define TVFS_FLAG_INCLUDE_CKEY 0x0001 // Include C-key in content file record #define TVFS_FLAG_WRITE_SUPPORT 0x0002 // Write support. Include a table of encoding specifiers. This is required for writing files to the underlying storage. This bit is implied by the patch-support bit #define TVFS_FLAG_PATCH_SUPPORT 0x0004 // Patch support. Include patch records in the content file records. #define TVFS_FLAG_LOWERCASE_MANIFEST 0x0008 // Lowercase manifest. All paths in the path table have been converted to ASCII lowercase (i.e. [A-Z] converted to [a-z]) #define TVFS_PTE_PATH_SEPARATOR_PRE 0x0001 // There is path separator before the name #define TVFS_PTE_PATH_SEPARATOR_POST 0x0002 // There is path separator after the name #define TVFS_PTE_NODE_VALUE 0x0004 // The NodeValue in path table entry is valid #define TVFS_FOLDER_NODE 0x80000000 // Highest bit is set if a file node is a folder #define TVFS_FOLDER_SIZE_MASK 0x7FFFFFFF // Mask to get length of the folder //----------------------------------------------------------------------------- // Local structures // In-memory layout of the TVFS file header typedef struct _TVFS_DIRECTORY_HEADER { DWORD Signature; // Must be CASC_TVFS_ROOT_SIGNATURE BYTE FormatVersion; // Version of the format. Should be 1. BYTE HeaderSize; // Size of the header, in bytes BYTE EKeySize; // Size of an E-Key. TACT uses 9-byte E-keys BYTE PatchKeySize; // Size of a patch key. TACT uses 9-byte P-keys DWORD Flags; // Flags. See TVFS_FLAG_XXX // Followed by the offset table (variable length) DWORD PathTableOffset; // Offset of the path table DWORD PathTableSize; // Size of the path table DWORD VfsTableOffset; // Offset of the VFS table DWORD VfsTableSize; // Size of the VFS table DWORD CftTableOffset; // Offset of the container file table DWORD CftTableSize; // Size of the container file table USHORT MaxDepth; // The maximum depth of the path prefix tree stored in the path table DWORD EstTableOffset; // The offset of the encoding specifier table. Only if the write-support bit is set in the header flag DWORD EstTableSize; // The size of the encoding specifier table. Only if the write-support bit is set in the header flag DWORD CftOffsSize; // Byte length of the offset in the Content File Table entry DWORD EstOffsSize; // Byte length of the offset in the Encoding Specifier Table entry LPBYTE pbDirectoryData; // Pointer to the begin of directory data LPBYTE pbDirectoryEnd; // Pointer to the end of directory data // LPBYTE pbPathFileTable; // Begin and end of the path table // LPBYTE pbPathTableEnd; // LPBYTE pbVfsFileTable; // Begin and end of the VFS file table // LPBYTE pbVfsTableEnd; // LPBYTE pbCftFileTable; // Begin and end of the content file table // LPBYTE pbCftTableEnd; } TVFS_DIRECTORY_HEADER, *PTVFS_DIRECTORY_HEADER; /* // Minimum size of a valid path table entry. 1 byte + 1-byte name + 1 byte + DWORD #define TVFS_HEADER_LENGTH FIELD_OFFSET(TVFS_DIRECTORY_HEADER, CftOffsSize) // Minimum size of a valid path table entry. 1 byte + 1-byte name + 1 byte + DWORD #define TVFS_MIN_PATH_ENTRY (1 + 1 + 1 + sizeof(DWORD)) // Minimum size of the VFS entry (SpanCount + FileOffset + SpanLength + CftOffset) #define TVFS_MIN_VFS_ENTRY (1 + sizeof(DWORD) + sizeof(DWORD) + 1) // Minimum size of the Content File Table entry (CASC_EKEY_SIZE + EncodedSize + ContentSize) #define TVFS_MIN_CFT_ENTRY (CASC_EKEY_SIZE + sizeof(DWORD) + sizeof(DWORD)) // Minimum size of the TVFS folder data #define TVFS_MIN_FILE_SIZE (TVFS_HEADER_LENGTH + TVFS_MIN_PATH_ENTRY + TVFS_MIN_VFS_ENTRY + TVFS_MIN_CFT_ENTRY) // Maximum estimated file table. Empirically set to 8 MB, increase if needed. #define TVFS_MAX_FILE_SIZE 0x00800000 */ // In-memory layout of the path table entry typedef struct _TVFS_PATH_TABLE_ENTRY { LPBYTE pbNamePtr; // Pointer to the begin of the node name LPBYTE pbNameEnd; // Pointer to the end of the file name DWORD NodeFlags; // TVFS_PTE_XXX DWORD NodeValue; // Node value } TVFS_PATH_TABLE_ENTRY, *PTVFS_PATH_TABLE_ENTRY; //----------------------------------------------------------------------------- // Handler definition for TVFS root file // Structure for the root handler struct TRootHandler_TVFS : public TFileTreeRoot { public: TRootHandler_TVFS() : TFileTreeRoot(0) { // TVFS supports file names, but DOESN'T support CKeys. dwFeatures |= CASC_FEATURE_FILE_NAMES; } // Returns size of "container file table offset" field in the VFS. // - If the container file table is larger than 0xffffff bytes, it's 4 bytes // - If the container file table is larger than 0xffff bytes, it's 3 bytes // - If the container file table is larger than 0xff bytes, it's 2 bytes // - If the container file table is smaller than 0xff bytes, it's 1 byte static DWORD GetOffsetFieldSize(DWORD dwTableSize) { if(dwTableSize > 0xffffff) return 4; if(dwTableSize > 0xffff) return 3; if(dwTableSize > 0xff) return 2; return 1; } bool PathBuffer_AppendNode(CASC_PATH & PathBuffer, TVFS_PATH_TABLE_ENTRY & PathEntry) { // Append the prefix separator, if needed if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_PRE) PathBuffer.AppendChar('/'); // Append the name fragment, if any if (PathEntry.pbNameEnd > PathEntry.pbNamePtr) PathBuffer.AppendStringN((const char *)PathEntry.pbNamePtr, (PathEntry.pbNameEnd - PathEntry.pbNamePtr), false); // Append the postfix separator, if needed if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_POST) PathBuffer.AppendChar('/'); return true; } static DWORD CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd) { // Fill the header structure with zeros memset(&DirHeader, 0, sizeof(TVFS_DIRECTORY_HEADER)); DirHeader.pbDirectoryData = pbDataPtr; DirHeader.pbDirectoryEnd = pbDataEnd; // Capture the signature pbDataPtr = CaptureInteger32(pbDataPtr, pbDataEnd, &DirHeader.Signature); if(pbDataPtr == NULL || DirHeader.Signature != CASC_TVFS_ROOT_SIGNATURE) return ERROR_BAD_FORMAT; // Capture the other four integers pbDataPtr = CaptureByteArray(pbDataPtr, pbDataEnd, 4, &DirHeader.FormatVersion); if(pbDataPtr == NULL || DirHeader.FormatVersion != 1 || DirHeader.EKeySize != 9 || DirHeader.PatchKeySize != 9 || DirHeader.HeaderSize < 8) return ERROR_BAD_FORMAT; // Capture the rest pbDataPtr = CaptureByteArray(pbDataPtr, pbDataEnd, DirHeader.HeaderSize - FIELD_OFFSET(TVFS_DIRECTORY_HEADER, Flags), (LPBYTE)(&DirHeader.Flags)); if(pbDataPtr == NULL) return ERROR_BAD_FORMAT; // Swap the header values DirHeader.Flags = ConvertBytesToInteger_4_LE((LPBYTE)(&DirHeader.Flags)); // Swap the offset table values DirHeader.PathTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.PathTableOffset)); DirHeader.PathTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.PathTableSize)); DirHeader.VfsTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.VfsTableOffset)); DirHeader.VfsTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.VfsTableSize)); DirHeader.CftTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.CftTableOffset)); DirHeader.CftTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.CftTableSize)); DirHeader.MaxDepth = (USHORT)ConvertBytesToInteger_2((LPBYTE)(&DirHeader.MaxDepth)); DirHeader.EstTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.EstTableOffset)); DirHeader.EstTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.EstTableSize)); // Determine size of file table offsets DirHeader.CftOffsSize = GetOffsetFieldSize(DirHeader.CftTableSize); DirHeader.EstOffsSize = GetOffsetFieldSize(DirHeader.EstTableSize); // Capture the path table // DirHeader.pbPathFileTable = pbDirectory + DirHeader.PathTableOffset; // DirHeader.pbPathTableEnd = pbDirectory + DirHeader.PathTableOffset + DirHeader.PathTableSize; // if(DirHeader.pbPathTableEnd > pbDataEnd) // return ERROR_BAD_FORMAT; // Capture the VFS file table // DirHeader.pbVfsFileTable = pbDirectory + DirHeader.VfsTableOffset; // DirHeader.pbVfsTableEnd = pbDirectory + DirHeader.VfsTableOffset + DirHeader.VfsTableSize; // if(DirHeader.pbVfsTableEnd > pbDataEnd) // return ERROR_BAD_FORMAT; // Capture the container file table // DirHeader.pbCftFileTable = pbDirectory + DirHeader.CftTableOffset; // DirHeader.pbCftTableEnd = pbDirectory + DirHeader.CftTableOffset + DirHeader.CftTableSize; // if(DirHeader.pbCftTableEnd > pbDataEnd) // return ERROR_BAD_FORMAT; return ERROR_SUCCESS; } LPBYTE CaptureVfsSpanCount(TVFS_DIRECTORY_HEADER & DirHeader, DWORD dwVfsOffset, DWORD & SpanCount) { LPBYTE pbVfsFileTable = DirHeader.pbDirectoryData + DirHeader.VfsTableOffset; LPBYTE pbVfsFileEntry = pbVfsFileTable + dwVfsOffset; LPBYTE pbVfsFileEnd = pbVfsFileTable + DirHeader.VfsTableSize; // Get the number of span entries if(!(pbVfsFileTable <= pbVfsFileEntry && pbVfsFileEntry < pbVfsFileEnd)) return NULL; SpanCount = *pbVfsFileEntry++; // 1 - 224 = valid file, 225-254 = other file, 255 = deleted file // We will ignore all files with unsupported span count return (1 <= SpanCount && SpanCount <= 224) ? pbVfsFileEntry : NULL; } LPBYTE CaptureVfsSpanEntries(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbVfsSpanEntry, PCASC_CKEY_ENTRY PtrSpanEntry, size_t SpanCount) { LPBYTE pbCftFileTable; LPBYTE pbCftFileEntry; LPBYTE pbCftFileEnd; LPBYTE pbVfsFileTable = DirHeader.pbDirectoryData + DirHeader.VfsTableOffset; LPBYTE pbVfsFileEnd = pbVfsFileTable + DirHeader.VfsTableSize; size_t ItemSize = sizeof(DWORD) + sizeof(DWORD) + DirHeader.CftOffsSize; // Check whether all spans are included in the valid range if(pbVfsSpanEntry < pbVfsFileTable || (pbVfsSpanEntry + (ItemSize * SpanCount)) > pbVfsFileEnd) return NULL; // Convert all spans for(size_t i = 0; i < SpanCount; i++) { DWORD dwCftOffset = ConvertBytesToInteger_X(pbVfsSpanEntry + sizeof(DWORD) + sizeof(DWORD), DirHeader.CftOffsSize); // // Structure of the span entry: // (4bytes): Offset into the referenced file (big endian) // (4bytes): Size of the span (big endian) // (?bytes): Offset into Container File Table. Length depends on container file table size // // Resolve the Container File Table entry pbCftFileTable = DirHeader.pbDirectoryData + DirHeader.CftTableOffset; pbCftFileEntry = pbCftFileTable + dwCftOffset; pbCftFileEnd = pbCftFileTable + DirHeader.CftTableSize; // Capture the EKey and the file size if((pbCftFileEntry + DirHeader.EKeySize + sizeof(DWORD)) > pbCftFileEnd) return NULL; // Copy the EKey and content size CaptureEncodedKey(PtrSpanEntry->EKey, pbCftFileEntry, DirHeader.EKeySize); PtrSpanEntry->ContentSize = ConvertBytesToInteger_4(pbVfsSpanEntry + sizeof(DWORD)); // Move to the next entry pbVfsSpanEntry += ItemSize; PtrSpanEntry++; } return pbVfsSpanEntry; } // // Structure of the path table entry: // (1byte) 0x00 (optional) - means that there will be prefix path separator // (1byte) File name length // (?byte) File name // (1byte) 0x00 (optional) - means that there will be postfix path separator // (1byte) 0xFF (optional) - node value identifier // (4byte) - node value // // Note: The path "data\archive\maps\file.bmp" could be cut into nodes like: // data\0 (or data with subdirectory) // arc // hive\0 // maps\0 (or folder data) // file.bmp // LPBYTE CapturePathEntry(TVFS_PATH_TABLE_ENTRY & PathEntry, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd) { // Reset the path entry structure PathEntry.pbNamePtr = pbPathTablePtr; PathEntry.pbNameEnd = pbPathTablePtr; PathEntry.NodeFlags = 0; PathEntry.NodeValue = 0; // Zero before the name means prefix path separator if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] == 0) { PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_PRE; pbPathTablePtr++; } // Capture the length of the name fragment if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] != 0xFF) { // Capture length of the name fragment size_t nLength = *pbPathTablePtr++; if ((pbPathTablePtr + nLength) > pbPathTableEnd) return NULL; PathEntry.pbNamePtr = pbPathTablePtr; PathEntry.pbNameEnd = pbPathTablePtr + nLength; pbPathTablePtr += nLength; } // Zero after the name means postfix path separator if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] == 0) { PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_POST; pbPathTablePtr++; } if (pbPathTablePtr < pbPathTableEnd) { // Check for node value if (pbPathTablePtr[0] == 0xFF) { if ((pbPathTablePtr + 1 + sizeof(DWORD)) > pbPathTableEnd) return NULL; PathEntry.NodeValue = ConvertBytesToInteger_4(pbPathTablePtr + 1); PathEntry.NodeFlags |= TVFS_PTE_NODE_VALUE; pbPathTablePtr = pbPathTablePtr + 1 + sizeof(DWORD); } // Non-0xFF after the name means path separator after else { PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_POST; assert(pbPathTablePtr[0] != 0); } } return pbPathTablePtr; } bool IsVfsFileEKey(TCascStorage * hs, LPBYTE EKey, size_t EKeyLength) { PCASC_CKEY_ENTRY pCKeyEntry; size_t ItemCount = hs->VfsRootList.ItemCount(); // Search the array for (size_t i = 0; i < ItemCount; i++) { pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i); if (pCKeyEntry != NULL) { if (!memcmp(pCKeyEntry->EKey, EKey, EKeyLength)) return true; } } // Not found in the VFS list return false; } // This function verifies whether a file is actually a sub-directory. // If yes, it contains just another "TVFS" virtual file system, just like the ROOT file. DWORD IsVfsSubDirectory(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, TVFS_DIRECTORY_HEADER & SubHeader, LPBYTE EKey, DWORD dwFileSize) { PCASC_CKEY_ENTRY pCKeyEntry; LPBYTE pbVfsData = NULL; DWORD cbVfsData = dwFileSize; DWORD dwErrCode = ERROR_BAD_FORMAT; // Verify whether the EKey is in the list of VFS root files if(IsVfsFileEKey(hs, EKey, DirHeader.EKeySize)) { // Locate the CKey entry if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey)) != NULL) { // Load the entire file into memory pbVfsData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbVfsData); if (pbVfsData && cbVfsData) { // Capture the file folder. This also serves as test dwErrCode = CaptureDirectoryHeader(SubHeader, pbVfsData, pbVfsData + cbVfsData); if (dwErrCode == ERROR_SUCCESS) return dwErrCode; // Clear the captured header memset(&SubHeader, 0, sizeof(TVFS_DIRECTORY_HEADER)); CASC_FREE(pbVfsData); } } } return dwErrCode; } PCASC_CKEY_ENTRY InsertUnknownCKeyEntry(TCascStorage * hs, LPBYTE pbEKey, size_t cbEKey, DWORD ContentSize) { PCASC_CKEY_ENTRY pCKeyEntry; // Insert a new entry to the array. DO NOT ALLOW enlarge array here pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false); if(pCKeyEntry != NULL) { memset(pCKeyEntry, 0, sizeof(CASC_CKEY_ENTRY)); memcpy(pCKeyEntry->EKey, pbEKey, cbEKey); pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64; pCKeyEntry->ContentSize = ContentSize; pCKeyEntry->EncodedSize = CASC_INVALID_SIZE; pCKeyEntry->Flags = CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL; pCKeyEntry->SpanCount = 1; // Copy the information from index files to the CKey entry CopyEKeyEntry(hs, pCKeyEntry); // Insert the item into EKey map hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey); } return pCKeyEntry; } void InsertRootVfsEntry(TCascStorage * hs, LPBYTE pbCKey, const char * szFormat, size_t nIndex) { PCASC_CKEY_ENTRY pCKeyEntry; char szFileName[0x20]; // The CKey entry must exist if((pCKeyEntry = FindCKeyEntry_CKey(hs, pbCKey)) != NULL) { CascStrPrintf(szFileName, _countof(szFileName), szFormat, nIndex); Insert(szFileName, pCKeyEntry); } } DWORD ParsePathFileTable(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, CASC_PATH & PathBuffer, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd) { TVFS_DIRECTORY_HEADER SubHeader; TVFS_PATH_TABLE_ENTRY PathEntry; PCASC_CKEY_ENTRY pCKeyEntry; LPBYTE pbVfsSpanEntry; size_t nSavePos = PathBuffer.Save(); DWORD dwSpanCount; DWORD dwErrCode; // Sanity check assert(SpanArray.IsInitialized()); // Parse the file table while(pbPathTablePtr < pbPathTableEnd) { // Capture the single path table entry pbPathTablePtr = CapturePathEntry(PathEntry, pbPathTablePtr, pbPathTableEnd); if(pbPathTablePtr == NULL) return ERROR_BAD_FORMAT; // Append the node name to the total path. Also add backslash, if it's a folder PathBuffer_AppendNode(PathBuffer, PathEntry); // Folder component if (PathEntry.NodeFlags & TVFS_PTE_NODE_VALUE) { // If the TVFS_FOLDER_NODE is set, then the path node is a directory, // with its data immediately following the path node. Lower 31 bits of NodeValue // contain the length of the directory (including the NodeValue!) if (PathEntry.NodeValue & TVFS_FOLDER_NODE) { LPBYTE pbDirectoryEnd = pbPathTablePtr + (PathEntry.NodeValue & TVFS_FOLDER_SIZE_MASK) - sizeof(DWORD); // Check the available data assert((PathEntry.NodeValue & TVFS_FOLDER_SIZE_MASK) >= sizeof(DWORD)); // Recursively call the folder parser on the same file dwErrCode = ParsePathFileTable(hs, DirHeader, PathBuffer, pbPathTablePtr, pbDirectoryEnd); if (dwErrCode != ERROR_SUCCESS) return dwErrCode; // Skip the directory data pbPathTablePtr = pbDirectoryEnd; } else { // Capture the number of VFS spans pbVfsSpanEntry = CaptureVfsSpanCount(DirHeader, PathEntry.NodeValue, dwSpanCount); if(pbVfsSpanEntry == NULL) return ERROR_BAD_FORMAT; // If it's one span, it's either a subdirectory or an entire file if(dwSpanCount == 1) { CASC_CKEY_ENTRY SpanEntry; // Capture the single span entry pbVfsSpanEntry = CaptureVfsSpanEntries(DirHeader, pbVfsSpanEntry, &SpanEntry, 1); if(pbVfsSpanEntry == NULL) return ERROR_FILE_CORRUPT; // Find the CKey entry pCKeyEntry = FindCKeyEntry_EKey(hs, SpanEntry.EKey); if(pCKeyEntry == NULL) { // Some files are in the ROOT manifest even if they are not in ENCODING and DOWNLOAD. // Example: "2018 - New CASC\00001", file "DivideAndConquer.w3m:war3mapMap.blp" pCKeyEntry = InsertUnknownCKeyEntry(hs, SpanEntry.EKey, DirHeader.EKeySize, SpanEntry.ContentSize); if(pCKeyEntry == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } } // We need to check whether this is another TVFS directory file if (IsVfsSubDirectory(hs, DirHeader, SubHeader, SpanEntry.EKey, SpanEntry.ContentSize) == ERROR_SUCCESS) { // Add colon (':') PathBuffer.AppendChar(':'); // The file content size should already be there assert(pCKeyEntry->ContentSize == SpanEntry.ContentSize); FileTree.InsertByName(pCKeyEntry, PathBuffer); // Parse the subdir ParseDirectoryData(hs, SubHeader, PathBuffer); CASC_FREE(SubHeader.pbDirectoryData); } else { // If the content content size is not there, supply it now if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE) pCKeyEntry->ContentSize = SpanEntry.ContentSize; FileTree.InsertByName(pCKeyEntry, PathBuffer); } } else { PCASC_CKEY_ENTRY pSpanEntries; PCASC_FILE_NODE pFileNode; USHORT RefCount; bool bFilePresent = true; // // Need to support multi-span files, possibly lager than 4 GB // Example: CoD: Black Ops 4, file "zone/base.xpak" 0x16 spans, over 15 GB size // // Allocate buffer for all span entries pSpanEntries = (PCASC_CKEY_ENTRY)SpanArray.Insert(dwSpanCount); if(pSpanEntries == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Capture all span entries pbVfsSpanEntry = CaptureVfsSpanEntries(DirHeader, pbVfsSpanEntry, pSpanEntries, dwSpanCount); if(pbVfsSpanEntry == NULL) return ERROR_FILE_CORRUPT; // Parse all span entries for(DWORD dwSpanIndex = 0; dwSpanIndex < dwSpanCount; dwSpanIndex++) { PCASC_CKEY_ENTRY pSpanEntry = pSpanEntries + dwSpanIndex; // Find the CKey entry pCKeyEntry = FindCKeyEntry_EKey(hs, pSpanEntries[dwSpanIndex].EKey); if(pCKeyEntry == NULL) { bFilePresent = false; break; } // Supply the content size if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE) pCKeyEntry->ContentSize = pSpanEntry->ContentSize; assert(pCKeyEntry->ContentSize == pSpanEntry->ContentSize); // Fill-in the span entry if(dwSpanIndex == 0) { pCKeyEntry->SpanCount = (BYTE)(dwSpanCount); pCKeyEntry->RefCount++; } else { // Mark the CKey entry as a file span. Note that a CKey entry // can actually be both a file span and a standalone file: // * zone/zm_red.xpak - { zone/zm_red.xpak_1, zone/zm_red.xpak_2, ..., zone/zm_red.xpak_6 } pCKeyEntry->Flags |= CASC_CE_FILE_SPAN; } // Copy all from the existing CKey entry memcpy(pSpanEntry, pCKeyEntry, sizeof(CASC_CKEY_ENTRY)); } // Do nothing if the file is not present locally if(bFilePresent) { // Insert a new file node that will contain pointer to the span entries RefCount = pSpanEntries->RefCount; pFileNode = FileTree.InsertByName(pSpanEntries, PathBuffer); pSpanEntries->RefCount = RefCount; if(pFileNode == NULL) return ERROR_NOT_ENOUGH_MEMORY; } } } // Reset the position of the path buffer PathBuffer.Restore(nSavePos); } } // Return the total number of entries return ERROR_SUCCESS; } DWORD ParseDirectoryData(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, CASC_PATH & PathBuffer) { LPBYTE pbRootDirectory = DirHeader.pbDirectoryData + DirHeader.PathTableOffset; LPBYTE pbRootDirPtr = pbRootDirectory; LPBYTE pbRootDirEnd = pbRootDirPtr + DirHeader.PathTableSize; DWORD dwNodeValue = 0; // Most usually, there is a root directory in the folder if((pbRootDirPtr + 1 + sizeof(DWORD)) < pbRootDirEnd) { // // The structure of the root directory // ----------------------------------- // 1byte 0xFF // 4bytes NodeValue (BigEndian). The most significant bit is set // - Lower 31 bits contain length of the directory data, including NodeValue // if(pbRootDirPtr[0] == 0xFF) { // Get the NodeValue and check its highest bit if(CaptureInteger32_BE(pbRootDirPtr + 1, pbRootDirEnd, &dwNodeValue) == NULL || (dwNodeValue & TVFS_FOLDER_NODE) == 0) return ERROR_BAD_FORMAT; // Get the range of the root directory pbRootDirEnd = pbRootDirPtr + 1 + (dwNodeValue & TVFS_FOLDER_SIZE_MASK); pbRootDirPtr = pbRootDirPtr + 1 + sizeof(DWORD); // Check the directory if(pbRootDirEnd > (pbRootDirectory + DirHeader.PathTableSize)) return ERROR_BAD_FORMAT; } } // Now go parse the path file table return ParsePathFileTable(hs, DirHeader, PathBuffer, pbRootDirPtr, pbRootDirEnd); } DWORD Load(TCascStorage * hs, TVFS_DIRECTORY_HEADER & RootHeader) { CASC_PATH PathBuffer; DWORD dwErrCode; // Save the length of the key FileTree.SetKeyLength(RootHeader.EKeySize); // Initialize the array of span entries dwErrCode = SpanArray.Create(sizeof(CASC_CKEY_ENTRY), 0x100); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Insert the main VFS root file as named entry InsertRootVfsEntry(hs, hs->VfsRoot.CKey, "vfs-root", 0); // Insert all VFS roots folders as files //for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++) //{ // pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i); // InsertRootVfsEntry(hs, pCKeyEntry->CKey, "vfs-%u", i+1); //} // Parse the entire directory data return ParseDirectoryData(hs, RootHeader, PathBuffer); } CASC_ARRAY SpanArray; // Array of CASC_SPAN_ENTRY for all multi-span files }; //----------------------------------------------------------------------------- // Public functions - TVFS root DWORD RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) { TRootHandler_TVFS * pRootHandler = NULL; TVFS_DIRECTORY_HEADER RootHeader; DWORD dwErrCode; // Capture the entire root directory dwErrCode = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, pbRootFile, pbRootFile + cbRootFile); if(dwErrCode == ERROR_SUCCESS) { // Allocate the root handler object pRootHandler = new TRootHandler_TVFS(); if(pRootHandler != NULL) { // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, RootHeader); if(dwErrCode != ERROR_SUCCESS) { delete pRootHandler; pRootHandler = NULL; } } } // Assign the root directory (or NULL) and return error hs->pRootHandler = pRootHandler; return dwErrCode; } ================================================ FILE: deps/CascLib/src/CascRootFile_Text.cpp ================================================ /*****************************************************************************/ /* CascRootFile_Text.cpp Copyright (c) Ladislav Zezula 2017 */ /*---------------------------------------------------------------------------*/ /* Support for generic ROOT handler with mapping of FileName -> CKey */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 28.10.15 1.00 Lad The first version of CascRootFile_Text.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Handler definitions for Starcraft I root file struct TRootHandler_SC1 : public TFileTreeRoot { public: TRootHandler_SC1() : TFileTreeRoot(0) { // We have file names and return CKey as result of search dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY); } static bool IsRootFile(LPBYTE pbRootFile, DWORD cbRootFile) { CASC_CSV Csv(1, false); size_t nColumns; bool bResult = false; // Get the first line from the listfile if(Csv.Load(pbRootFile, cbRootFile) == ERROR_SUCCESS) { // There must be 2 or 3 elements nColumns = Csv[CSV_ZERO].GetColumnCount(); if (nColumns == 2 || nColumns == 3) { const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][CSV_ZERO]; const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][1]; bResult = (FileName.szValue && CKeyStr.szValue && CKeyStr.nLength == MD5_STRING_SIZE); } } // We need to reset the listfile to the begin position return bResult; } DWORD Load(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) { PCASC_CKEY_ENTRY pCKeyEntry; CASC_CSV Csv(0, false); BYTE CKey[MD5_HASH_SIZE]; DWORD dwErrCode; // Parse the ROOT file first in order to see whether we have the correct format dwErrCode = Csv.Load(pbRootFile, cbRootFile); if(dwErrCode == ERROR_SUCCESS) { // Parse all lines while(Csv.LoadNextLine()) { const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][CSV_ZERO]; const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][1]; // Convert the CKey to binary if(BinaryFromString(CKeyStr.szValue, MD5_STRING_SIZE, CKey) == ERROR_SUCCESS) { // Verify whether it is a known entry if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKey)) != NULL) { // Insert the FileName+CKey to the file tree FileTree.InsertByName(pCKeyEntry, FileName.szValue); } } } } return dwErrCode; } }; //----------------------------------------------------------------------------- // Public functions // // Starcraft ROOT file is a text file with the following format: // HD2/portraits/NBluCrit/NLCFID01.webm|c2795b120592355d45eba9cdc37f691e // locales/enUS/Assets/campaign/EXPZerg/Zerg08/staredit/wav/zovtra01.ogg|316b0274bf2dabaa8db60c3ff1270c85 // locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c // DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) { TRootHandler_SC1 * pRootHandler = NULL; DWORD dwErrCode = ERROR_BAD_FORMAT; // Verify whether this looks like a Starcraft I root file if(TRootHandler_SC1::IsRootFile(pbRootFile, cbRootFile)) { // Allocate the root handler object pRootHandler = new TRootHandler_SC1(); if(pRootHandler != NULL) { // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, pbRootFile, cbRootFile); if(dwErrCode != ERROR_SUCCESS) { delete pRootHandler; pRootHandler = NULL; } } } // Assign the root directory (or NULL) and return error hs->pRootHandler = pRootHandler; return dwErrCode; } ================================================ FILE: deps/CascLib/src/CascRootFile_WoW.cpp ================================================ /*****************************************************************************/ /* CascRootFile_WoW.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Storage functions for CASC */ /* Note: WoW offsets refer to WoW.exe 6.0.3.19116 (32-bit) */ /* SHA1: c10e9ffb7d040a37a356b96042657e1a0c95c0dd */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad The first version of CascRootFile_WoW.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // Local structures #define ROOT_SEARCH_PHASE_INITIALIZING 0 #define ROOT_SEARCH_PHASE_LISTFILE 1 #define ROOT_SEARCH_PHASE_NAMELESS 2 #define ROOT_SEARCH_PHASE_FINISHED 3 // Known dwRegion values returned from sub_661316 (7.0.3.22210 x86 win), also referred by lua GetCurrentRegion #define WOW_REGION_US 0x01 #define WOW_REGION_KR 0x02 #define WOW_REGION_EU 0x03 #define WOW_REGION_TW 0x04 #define WOW_REGION_CN 0x05 typedef enum _ROOT_FORMAT { RootFormatWoW6x, // WoW 6.x - 8.1.x RootFormatWoW82 // WoW 8.2 or newer } ROOT_FORMAT, *PROOT_FORMAT; // ROOT file header, since 8.2 typedef struct _FILE_ROOT_HEADER_82 { DWORD Signature; // Must be CASC_WOW82_ROOT_SIGNATURE DWORD TotalFiles; DWORD FilesWithNameHash; } FILE_ROOT_HEADER_82, *PFILE_ROOT_HEADER_82; // On-disk version of root group. A root group contains a group of file // with the same locale and file flags typedef struct _FILE_ROOT_GROUP_HEADER { DWORD NumberOfFiles; // Number of entries DWORD ContentFlags; DWORD LocaleFlags; // File locale mask (CASC_LOCALE_XXX) // Followed by a block of file data IDs (count: NumberOfFiles) // Followed by the MD5 and file name hash (count: NumberOfFiles) } FILE_ROOT_GROUP_HEADER, *PFILE_ROOT_GROUP_HEADER; // On-disk version of root entry. Only present in versions 6.x - 8.1.xx // Each root entry represents one file in the CASC storage // In WoW 8.2 and newer, CKey and FileNameHash are split into separate arrays // and FileNameHash is optional typedef struct _FILE_ROOT_ENTRY { CONTENT_KEY CKey; // MD5 of the file ULONGLONG FileNameHash; // Jenkins hash of the file name } FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY; typedef struct _FILE_ROOT_GROUP { FILE_ROOT_GROUP_HEADER Header; PDWORD FileDataIds; // Pointer to the array of File Data IDs PFILE_ROOT_ENTRY pRootEntries; // Valid for WoW 6.x - 8.1.x PCONTENT_KEY pCKeyEntries; // Valid for WoW 8.2 or newer PULONGLONG pHashes; // Valid for WoW 8.2 or newer (optional) } FILE_ROOT_GROUP, *PFILE_ROOT_GROUP; //----------------------------------------------------------------------------- // TRootHandler_WoW interface / implementation #define FTREE_FLAGS_WOW (FTREE_FLAG_USE_DATA_ID | FTREE_FLAG_USE_LOCALE_FLAGS | FTREE_FLAG_USE_CONTENT_FLAGS) struct TRootHandler_WoW : public TFileTreeRoot { public: TRootHandler_WoW(ROOT_FORMAT RFormat, DWORD HashlessFileCount) : TFileTreeRoot(FTREE_FLAGS_WOW) { // Turn off the "we know file names" bit FileCounterHashless = HashlessFileCount; FileCounter = 0; RootFormat = RFormat; // Update the flags based on format switch(RootFormat) { case RootFormatWoW6x: dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_FNAME_HASHES | CASC_FEATURE_FILE_DATA_IDS | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS; break; case RootFormatWoW82: dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_FNAME_HASHES_OPTIONAL | CASC_FEATURE_FILE_DATA_IDS | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS; break; } } static LPBYTE CaptureRootHeader(FILE_ROOT_HEADER_82 & RootHeader, LPBYTE pbRootPtr, LPBYTE pbRootEnd) { // Validate the root file header if((pbRootPtr + sizeof(FILE_ROOT_HEADER_82)) >= pbRootEnd) return NULL; memcpy(&RootHeader, pbRootPtr, sizeof(FILE_ROOT_HEADER_82)); // Verify the root file header if(RootHeader.Signature != CASC_WOW82_ROOT_SIGNATURE) return NULL; if(RootHeader.FilesWithNameHash > RootHeader.TotalFiles) return NULL; return pbRootPtr + sizeof(FILE_ROOT_HEADER_82); } LPBYTE CaptureRootGroup(FILE_ROOT_GROUP & RootGroup, LPBYTE pbRootPtr, LPBYTE pbRootEnd) { // Reset the entire root group structure memset(&RootGroup, 0, sizeof(FILE_ROOT_GROUP)); // Validate the locale block header if((pbRootPtr + sizeof(FILE_ROOT_GROUP_HEADER)) >= pbRootEnd) return NULL; memcpy(&RootGroup.Header, pbRootPtr, sizeof(FILE_ROOT_GROUP_HEADER)); pbRootPtr = pbRootPtr + sizeof(FILE_ROOT_GROUP_HEADER); // Validate the array of file data IDs if((pbRootPtr + (sizeof(DWORD) * RootGroup.Header.NumberOfFiles)) >= pbRootEnd) return NULL; RootGroup.FileDataIds = (PDWORD)pbRootPtr; pbRootPtr = pbRootPtr + (sizeof(DWORD) * RootGroup.Header.NumberOfFiles); // Add the number of files in this block to the number of files loaded FileCounter += RootGroup.Header.NumberOfFiles; // Validate the array of root entries switch(RootFormat) { case RootFormatWoW6x: if((pbRootPtr + (sizeof(FILE_ROOT_ENTRY) * RootGroup.Header.NumberOfFiles)) > pbRootEnd) return NULL; RootGroup.pRootEntries = (PFILE_ROOT_ENTRY)pbRootPtr; // Return the position of the next block return pbRootPtr + (sizeof(FILE_ROOT_ENTRY) * RootGroup.Header.NumberOfFiles); case RootFormatWoW82: // Verify the position of array of CONTENT_KEY if((pbRootPtr + (sizeof(CONTENT_KEY) * RootGroup.Header.NumberOfFiles)) > pbRootEnd) return NULL; RootGroup.pCKeyEntries = (PCONTENT_KEY)pbRootPtr; pbRootPtr = pbRootPtr + (sizeof(CONTENT_KEY) * RootGroup.Header.NumberOfFiles); // Also include array of file hashes if(!(RootGroup.Header.ContentFlags & CASC_CFLAG_NO_NAME_HASH)) { if((pbRootPtr + (sizeof(ULONGLONG) * RootGroup.Header.NumberOfFiles)) > pbRootEnd) return NULL; RootGroup.pHashes = (PULONGLONG)pbRootPtr; pbRootPtr = pbRootPtr + (sizeof(ULONGLONG) * RootGroup.Header.NumberOfFiles); } return pbRootPtr; default: return NULL; } } DWORD ParseWowRootFile_AddFiles_6x(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup) { PFILE_ROOT_ENTRY pRootEntry = RootGroup.pRootEntries; PCASC_CKEY_ENTRY pCKeyEntry; DWORD FileDataId = 0; // Sanity check assert(RootGroup.pRootEntries != NULL); // WoW.exe (build 19116): Blocks with zero files are skipped for(DWORD i = 0; i < RootGroup.Header.NumberOfFiles; i++, pRootEntry++) { // Set the file data ID FileDataId = FileDataId + RootGroup.FileDataIds[i]; // BREAKIF(FileDataId == 2823765); // Find the item in the central storage. Insert it to the tree if((pCKeyEntry = FindCKeyEntry_CKey(hs, pRootEntry->CKey.Value)) != NULL) { if(pRootEntry->FileNameHash != 0) { FileTree.InsertByHash(pCKeyEntry, pRootEntry->FileNameHash, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags); } else { FileTree.InsertById(pCKeyEntry, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags); } } // Update the file data ID assert((FileDataId + 1) > FileDataId); FileDataId++; } return ERROR_SUCCESS; } DWORD ParseWowRootFile_AddFiles_82(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup) { PCASC_CKEY_ENTRY pCKeyEntry; PCONTENT_KEY pCKey = RootGroup.pCKeyEntries; DWORD FileDataId = 0; // Sanity check assert(RootGroup.pCKeyEntries != NULL); // WoW.exe (build 19116): Blocks with zero files are skipped for(DWORD i = 0; i < RootGroup.Header.NumberOfFiles; i++, pCKey++) { // Set the file data ID FileDataId = FileDataId + RootGroup.FileDataIds[i]; //printf("File Data ID: %u\n", FileDataId); // Find the item in the central storage. Insert it to the tree if((pCKeyEntry = FindCKeyEntry_CKey(hs, pCKey->Value)) != NULL) { // If we know the file name hash, we're gonna insert it by hash AND file data id. // If we don't know the hash, we're gonna insert it just by file data id. if(RootGroup.pHashes != NULL && RootGroup.pHashes[i] != 0) { FileTree.InsertByHash(pCKeyEntry, RootGroup.pHashes[i], FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags); } else { FileTree.InsertById(pCKeyEntry, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags); } } // Update the file data ID assert((FileDataId + 1) > FileDataId); FileDataId++; } return ERROR_SUCCESS; } DWORD ParseWowRootFile_Level2( TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask, BYTE bOverrideLowViolence, BYTE bAudioLocale) { FILE_ROOT_GROUP RootBlock; // Reset the total file counter FileCounter = 0; // Now parse the root file while(pbRootPtr < pbRootEnd) { //char szMessage[0x100]; //StringCchPrintfA(szMessage, _countof(szMessage), "%p\n", (pbRootEnd - pbRootPtr)); //OutputDebugStringA(szMessage); // Validate the file locale block pbRootPtr = CaptureRootGroup(RootBlock, pbRootPtr, pbRootEnd); if(pbRootPtr == NULL) return ERROR_BAD_FORMAT; // WoW.exe (build 19116): Entries with flag 0x100 set are skipped if(RootBlock.Header.ContentFlags & CASC_CFLAG_DONT_LOAD) continue; // WoW.exe (build 19116): Entries with flag 0x80 set are skipped if overrideArchive CVAR is set to FALSE (which is by default in non-chinese clients) if((RootBlock.Header.ContentFlags & CASC_CFLAG_LOW_VIOLENCE) && bOverrideLowViolence == 0) continue; // WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to bAudioLocale are skipped if((RootBlock.Header.ContentFlags >> 0x1F) != bAudioLocale) continue; // WoW.exe (build 19116): Locales other than defined mask are skipped too if(RootBlock.Header.LocaleFlags != 0 && (RootBlock.Header.LocaleFlags & dwLocaleMask) == 0) continue; // Now call the custom function switch(RootFormat) { case RootFormatWoW82: ParseWowRootFile_AddFiles_82(hs, RootBlock); break; case RootFormatWoW6x: ParseWowRootFile_AddFiles_6x(hs, RootBlock); break; default: return ERROR_NOT_SUPPORTED; } } return ERROR_SUCCESS; } /* #define CASC_LOCALE_BIT_ENUS 0x01 #define CASC_LOCALE_BIT_KOKR 0x02 #define CASC_LOCALE_BIT_RESERVED 0x03 #define CASC_LOCALE_BIT_FRFR 0x04 #define CASC_LOCALE_BIT_DEDE 0x05 #define CASC_LOCALE_BIT_ZHCN 0x06 #define CASC_LOCALE_BIT_ESES 0x07 #define CASC_LOCALE_BIT_ZHTW 0x08 #define CASC_LOCALE_BIT_ENGB 0x09 #define CASC_LOCALE_BIT_ENCN 0x0A #define CASC_LOCALE_BIT_ENTW 0x0B #define CASC_LOCALE_BIT_ESMX 0x0C #define CASC_LOCALE_BIT_RURU 0x0D #define CASC_LOCALE_BIT_PTBR 0x0E #define CASC_LOCALE_BIT_ITIT 0x0F #define CASC_LOCALE_BIT_PTPT 0x10 // dwLocale is obtained from a WOW_LOCALE_* to CASC_LOCALE_BIT_* mapping (sub_6615D0 in 7.0.3.22210 x86 win) // because (ENUS, ENGB) and (PTBR, PTPT) pairs share the same value on WOW_LOCALE_* enum // dwRegion is used to distinguish them if(dwRegion == WOW_REGION_EU) { // Is this english version of WoW? if(dwLocale == CASC_LOCALE_BIT_ENUS) { LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_ENGB, bOverrideArchive, bAudioLocale); LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_ENUS, bOverrideArchive, bAudioLocale); return ERROR_SUCCESS; } // Is this portuguese version of WoW? if(dwLocale == CASC_LOCALE_BIT_PTBR) { LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_PTPT, bOverrideArchive, bAudioLocale); LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_PTBR, bOverrideArchive, bAudioLocale); } } else LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, (1 << dwLocale), bOverrideArchive, bAudioLocale); */ DWORD ParseWowRootFile_Level1( TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask, BYTE bAudioLocale) { DWORD dwErrCode; // Load the locale as-is dwErrCode = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale); if (dwErrCode != ERROR_SUCCESS) return dwErrCode; // If we wanted enGB, we also load enUS for the missing files if(dwLocaleMask == CASC_LOCALE_ENGB) ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_ENUS, false, bAudioLocale); if(dwLocaleMask == CASC_LOCALE_PTPT) ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_PTBR, false, bAudioLocale); return ERROR_SUCCESS; } // WoW.exe: 004146C7 (BuildManifest::Load) DWORD Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask) { DWORD dwErrCode; dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0); if (dwErrCode == ERROR_SUCCESS) dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1); return dwErrCode; } // Search for files PCASC_CKEY_ENTRY Search(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) { // If we have a listfile, we'll feed the listfile entries to the file tree if(pSearch->pCache != NULL && pSearch->bListFileUsed == false) { PCASC_FILE_NODE pFileNode; ULONGLONG FileNameHash; size_t nLength; DWORD FileDataId = CASC_INVALID_ID; char szFileName[MAX_PATH]; if(RootFormat == RootFormatWoW82) { // Keep going through the listfile for(;;) { // Retrieve the next line from the list file. Ignore lines that are too long to fit in the buffer nLength = ListFile_GetNext(pSearch->pCache, szFileName, _countof(szFileName), &FileDataId); if(nLength == 0) { if(GetCascError() == ERROR_INSUFFICIENT_BUFFER) continue; break; } // Try to find the file node by file data id pFileNode = FileTree.FindById(FileDataId); if(pFileNode != NULL && pFileNode->NameLength == 0) { FileTree.SetNodeFileName(pFileNode, szFileName); } } } else { // Keep going through the listfile for(;;) { // Retrieve the next line from the list file. Ignore lines that are too long to fit in the buffer nLength = ListFile_GetNextLine(pSearch->pCache, szFileName, _countof(szFileName)); if(nLength == 0) { if(GetCascError() == ERROR_INSUFFICIENT_BUFFER) continue; break; } // Calculate the hash of the file name FileNameHash = CalcFileNameHash(szFileName); // Try to find the file node by file name hash pFileNode = FileTree.Find(FileNameHash); if(pFileNode != NULL && pFileNode->NameLength == 0) { FileTree.SetNodeFileName(pFileNode, szFileName); } } } pSearch->bListFileUsed = true; } // Let the file tree root give us the file names return TFileTreeRoot::Search(pSearch, pFindData); } ROOT_FORMAT RootFormat; // Root file format DWORD FileCounterHashless; // Number of files for which we don't have hash. Meaningless for WoW before 8.2 DWORD FileCounter; // Counter of loaded files. Only used during loading of ROOT file }; //----------------------------------------------------------------------------- // Public functions DWORD RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask) { TRootHandler_WoW * pRootHandler = NULL; FILE_ROOT_HEADER_82 RootHeader; ROOT_FORMAT RootFormat = RootFormatWoW6x; LPBYTE pbRootEnd = pbRootFile + cbRootFile; LPBYTE pbRootPtr; DWORD FileCounterHashless = 0; DWORD dwErrCode = ERROR_BAD_FORMAT; // Check for the new format (World of Warcraft 8.2, build 30170) pbRootPtr = TRootHandler_WoW::CaptureRootHeader(RootHeader, pbRootFile, pbRootEnd); if(pbRootPtr != NULL) { FileCounterHashless = RootHeader.TotalFiles - RootHeader.FilesWithNameHash; RootFormat = RootFormatWoW82; pbRootFile = pbRootPtr; } // Create the WOW handler pRootHandler = new TRootHandler_WoW(RootFormat, FileCounterHashless); if(pRootHandler != NULL) { // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, pbRootFile, pbRootEnd, dwLocaleMask); if(dwErrCode != ERROR_SUCCESS) { delete pRootHandler; pRootHandler = NULL; } } // Assign the root directory (or NULL) and return error hs->pRootHandler = pRootHandler; return dwErrCode; } ================================================ FILE: deps/CascLib/src/CascStructs.h ================================================ /*****************************************************************************/ /* CascStructs.h Copyright (c) Ladislav Zezula 2019 */ /*---------------------------------------------------------------------------*/ /* On-disk structures for CASC storages */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 28.04.19 1.00 Lad The first version of CascStructs.h */ /*****************************************************************************/ #ifndef __CASC_STRUCTS_H__ #define __CASC_STRUCTS_H__ //----------------------------------------------------------------------------- // Common definitions #define CASC_INDEX_COUNT 0x10 // Number of index files #define CASC_CKEY_SIZE 0x10 // Size of the content key #define CASC_EKEY_SIZE 0x09 // Size of the encoded key #define CASC_MAX_DATA_FILES 0x100 // Maximum number of data files //----------------------------------------------------------------------------- // The index files structures #define FILE_INDEX_PAGE_SIZE 0x200 // Number of bytes in one page of EKey items // Structure describing the 32-bit block size and 32-bit Jenkins hash of the block typedef struct _BLOCK_SIZE_AND_HASH { DWORD cbBlockSize; DWORD dwBlockHash; } BLOCK_SIZE_AND_HASH, *PBLOCK_SIZE_AND_HASH; // Checksum describing a block in the index file (v2) typedef struct _FILE_INDEX_GUARDED_BLOCK { DWORD BlockSize; DWORD BlockHash; } FILE_INDEX_GUARDED_BLOCK, *PFILE_INDEX_GUARDED_BLOCK; // Structure of the header of the index files version 1 typedef struct _FILE_INDEX_HEADER_V1 { USHORT IndexVersion; // Must be 0x05 BYTE BucketIndex; // The bucket index of this file; should be the same as the first byte of the hex filename. BYTE align_3; DWORD field_4; ULONGLONG field_8; ULONGLONG SegmentSize; // Size of one data segment (aka data.### file) BYTE EncodedSizeLength; // Length, in bytes, of the EncodedSize in the EKey entry BYTE StorageOffsetLength; // Length, in bytes, of the StorageOffset field in the EKey entry BYTE EKeyLength; // Length of the encoded key (bytes) BYTE FileOffsetBits; // Number of bits of the archive file offset in StorageOffset field. Rest is data segment index DWORD EKeyCount1; DWORD EKeyCount2; DWORD KeysHash1; DWORD KeysHash2; DWORD HeaderHash; } FILE_INDEX_HEADER_V1, *PFILE_INDEX_HEADER_V1; typedef struct _FILE_INDEX_HEADER_V2 { USHORT IndexVersion; // Must be 0x07 BYTE BucketIndex; // The bucket index of this file; should be the same as the first byte of the hex filename. BYTE ExtraBytes; // Unknown; must be 0 BYTE EncodedSizeLength; // Length, in bytes, of the EncodedSize in the EKey entry BYTE StorageOffsetLength; // Length, in bytes, of the StorageOffset field in the EKey entry BYTE EKeyLength; // Length of the encoded key (bytes) BYTE FileOffsetBits; // Number of bits of the archive file offset in StorageOffset field. Rest is data segment index ULONGLONG SegmentSize; // Size of one data segment (aka data.### file) } FILE_INDEX_HEADER_V2, *PFILE_INDEX_HEADER_V2; // The EKey entry from the ".idx" files. Note that the lengths are optional and controlled by the FILE_INDEX_HEADER_Vx typedef struct _FILE_EKEY_ENTRY { BYTE EKey[CASC_EKEY_SIZE]; // The first 9 bytes of the encoded key BYTE FileOffsetBE[5]; // Index of data file and offset within (big endian). BYTE EncodedSize[4]; // Encoded size (big endian). This is the size of encoded header, all file frame headers and all file frames } FILE_EKEY_ENTRY, *PFILE_EKEY_ENTRY; //----------------------------------------------------------------------------- // The archive index (md5.index) files structures // https://wowdev.wiki/TACT#CDN_File_Organization template struct FILE_INDEX_FOOTER { BYTE TocHash[MD5_HASH_SIZE]; // Client tries to read with 0x10, then backs off in size when smaller BYTE Version; // Version of the index header BYTE Reserved[2]; // Length, in bytes, of the file offset field BYTE PageSizeKB; // Length, in kilobytes, of the index page BYTE OffsetBytes; // Normally 4 for archive indices, 6 for group indices, and 0 for loose file indices BYTE SizeBytes; // Normally 4 BYTE EKeyLength; // Normally 16 BYTE FooterHashBytes; // Normally 8, <= 0x10 BYTE ElementCount[4]; // BigEndian in _old_ versions (e.g. 18179) BYTE FooterHash[CHKSUM_LENGTH]; }; //----------------------------------------------------------------------------- // The ENCODING manifest structures // // The ENCODING file is in the form of: // * File header. Fixed length. // * Encoding Specification (ESpec) in the string form. Length is stored in FILE_ENCODING_HEADER::ESpecBlockSize // https://wowdev.wiki/CASC#Encoding // https://wowdev.wiki/TACT#Encoding_table // #define FILE_MAGIC_ENCODING 0x4E45 // 'EN' // File header of the ENCODING manifest typedef struct _FILE_ENCODING_HEADER { USHORT Magic; // FILE_MAGIC_ENCODING ('EN') BYTE Version; // Expected to be 1 by CascLib BYTE CKeyLength; // The content key length in ENCODING file. Usually 0x10 BYTE EKeyLength; // The encoded key length in ENCODING file. Usually 0x10 BYTE CKeyPageSize[2]; // Size of the CKey page, in KB (big-endian) BYTE EKeyPageSize[2]; // Size of the EKey page, in KB (big-endian) BYTE CKeyPageCount[4]; // Number of CKey pages in the table (big endian) BYTE EKeyPageCount[4]; // Number of EKey pages in the table (big endian) BYTE field_11; // Asserted to be zero by the agent BYTE ESpecBlockSize[4]; // Size of the ESpec string block } FILE_ENCODING_HEADER, *PFILE_ENCODING_HEADER; // Page header of the ENCODING manifest typedef struct _FILE_CKEY_PAGE { BYTE FirstKey[MD5_HASH_SIZE]; // The first CKey/EKey in the segment BYTE SegmentHash[MD5_HASH_SIZE]; // MD5 hash of the entire segment } FILE_CKEY_PAGE, *PFILE_CKEY_PAGE; // Single entry in the page typedef struct _FILE_CKEY_ENTRY { USHORT EKeyCount; // Number of EKeys BYTE ContentSize[4]; // Content file size (big endian) BYTE CKey[CASC_CKEY_SIZE]; // Content key. This is MD5 of the file content BYTE EKey[CASC_CKEY_SIZE]; // Encoded key. This is (trimmed) MD5 hash of the file header, containing MD5 hashes of all the logical blocks of the file } FILE_CKEY_ENTRY, *PFILE_CKEY_ENTRY; typedef struct _FILE_ESPEC_ENTRY { BYTE ESpecKey[MD5_HASH_SIZE]; // The ESpec key of the file BYTE ESpecIndexBE[4]; // Index of ESPEC entry, assuming zero-terminated strings (big endian) BYTE FileSizeBE[5]; // Size of the encoded version of the file (big endian) } FILE_ESPEC_ENTRY, *PFILE_ESPEC_ENTRY; //----------------------------------------------------------------------------- // The DOWNLOAD manifest structures // // See https://wowdev.wiki/TACT#Download_manifest // #define FILE_MAGIC_DOWNLOAD 0x4C44 // 'DL' // File header of the DOWNLOAD manifest typedef struct _FILE_DOWNLOAD_HEADER { USHORT Magic; // FILE_MAGIC_DOWNLOAD ('DL') BYTE Version; // Expected to be 1 by CascLib BYTE EKeyLength; // The content key length in DOWNLOAD file. Expected to be 0x10 BYTE EntryHasChecksum; // If nonzero, then the entry has checksum. BYTE EntryCount[4]; // Number of entries (big-endian) BYTE TagCount[2]; // Number of tag entries (big endian) // Version 2 or newer BYTE FlagByteSize; // Number of flag bytes // Verion 3 or newer BYTE BasePriority; BYTE Unknown2[3]; } FILE_DOWNLOAD_HEADER, *PFILE_DOWNLOAD_HEADER; typedef struct _FILE_DOWNLOAD_ENTRY { BYTE EKey[MD5_HASH_SIZE]; // Encoding key (variable length) BYTE FileSize[5]; // File size BYTE Priority; // DWORD Checksum [optional] // BYTE Flags; } FILE_DOWNLOAD_ENTRY, *PFILE_DOWNLOAD_ENTRY; //----------------------------------------------------------------------------- // The INSTALL manifest structures // // See https://wowdev.wiki/TACT#Install_manifest // #define FILE_MAGIC_INSTALL 0x4E49 // 'IN' // File header of the INSTALL manifest typedef struct _FILE_INSTALL_HEADER { USHORT Magic; // FILE_MAGIC_INSTALL ('DL') BYTE Version; // Expected to be 1 by CascLib BYTE EKeyLength; // The content key length in INSTALL file. Expected to be 0x10 BYTE TagCount[2]; // Number of tag entries (big endian) BYTE EntryCount[4]; // Number of entries (big-endian) } FILE_INSTALL_HEADER, *PFILE_INSTALL_HEADER; //----------------------------------------------------------------------------- // Data file structures #define BLTE_HEADER_SIGNATURE 0x45544C42 // 'BLTE' header in the data files #define BLTE_HEADER_DELTA 0x1E // Distance of BLTE header from begin of the header area #define MAX_ENCODED_HEADER 0x1000 // Starting size for the frame headers typedef struct _BLTE_HEADER { BYTE Signature[4]; // Must be "BLTE" BYTE HeaderSize[4]; // Header size in bytes (big endian) BYTE MustBe0F; // Must be 0x0F. Optional, only if HeaderSize != 0 BYTE FrameCount[3]; // Frame count (big endian). Optional, only if HeaderSize != 0 } BLTE_HEADER, *PBLTE_HEADER; typedef struct _BLTE_ENCODED_HEADER { // Header span. ENCODED_KEY EKey; // Encoded key of the data beginning with "BLTE" (byte-reversed) DWORD EncodedSize; // Encoded size of the data data beginning with "BLTE" (little endian) BYTE field_14; // Seems to be 1 if the header span has no data BYTE field_15; // Hardcoded to zero (Agent.exe 2.15.0.6296: 01370000->0148E2AA) BYTE JenkinsHash[4]; // Jenkins hash (hashlittle2) of the preceding fields (EKey + EncodedSize + field_14 + field_15) (little endian) BYTE Checksum[4]; // Checksum of the previous part. See "VerifyHeaderSpan()" for more information. // BLTE header. Always present. BYTE Signature[4]; // Must be "BLTE" BYTE HeaderSize[4]; // Header size in bytes (big endian) BYTE MustBe0F; // Must be 0x0F. Optional, only if HeaderSize != 0 BYTE FrameCount[3]; // Frame count (big endian). Optional, only if HeaderSize != 0 } BLTE_ENCODED_HEADER, *PBLTE_ENCODED_HEADER; typedef struct _BLTE_FRAME { BYTE EncodedSize[4]; // Encoded frame size (big endian) BYTE ContentSize[4]; // Content frame size (big endian) CONTENT_KEY FrameHash; // Hash of the encoded frame } BLTE_FRAME, *PBLTE_FRAME; #endif // __CASC_STRUCTS_H__ ================================================ FILE: deps/CascLib/src/DllMain.c ================================================ /*****************************************************************************/ /* DllMain.c Copyright (c) Ladislav Zezula 2015 */ /*---------------------------------------------------------------------------*/ /* Description: DllMain for the CascLib.dll library */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 26.10.15 1.00 Lad The first version of DllMain.c */ /*****************************************************************************/ #define WIN32_LEAN_AND_MEAN #include //----------------------------------------------------------------------------- // DllMain BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) { UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(dwReason); UNREFERENCED_PARAMETER(lpReserved); return TRUE; } ================================================ FILE: deps/CascLib/src/DllMain.def ================================================ ; ; Export file for Windows ; Copyright (c) 2015 Ladislav Zezula ; ladik@zezula.net ; LIBRARY CascLib.dll EXPORTS CascOpenStorage CascOpenStorageEx CascOpenOnlineStorage CascGetStorageInfo CascCloseStorage CascOpenFile CascOpenLocalFile CascGetFileInfo CascGetFileSize CascGetFileSize64 CascSetFilePointer CascSetFilePointer64 CascReadFile CascCloseFile CascFindFirstFile CascFindNextFile CascFindClose CascAddEncryptionKey CascAddStringEncryptionKey CascFindEncryptionKey CascGetNotFoundEncryptionKey GetCascError SetCascError ================================================ FILE: deps/CascLib/src/DllMain.rc ================================================ // Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // Neutral resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #pragma code_page(1250) ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 1,50,0,205 PRODUCTVERSION 1,50,0,205 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040504b0" BEGIN VALUE "Comments", "http://www.zezula.net/casc.html" VALUE "FileDescription", "CascLib library for reading Blizzard CASC storages" VALUE "FileVersion", "1, 50, 0, 205" VALUE "InternalName", "CascLib" VALUE "LegalCopyright", "Copyright (c) 2014 - 2021 Ladislav Zezula" VALUE "OriginalFilename", "CascLib.dll" VALUE "ProductName", "CascLib" VALUE "ProductVersion", "1, 50, 0, 205" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x405, 1200 END END #endif // Neutral resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Czech (Czech Republic) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CSY) LANGUAGE LANG_CZECH, SUBLANG_DEFAULT #pragma code_page(1250) #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 2 TEXTINCLUDE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END 1 TEXTINCLUDE BEGIN "resource.h\0" END #endif // APSTUDIO_INVOKED #endif // Czech (Czech Republic) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ================================================ FILE: deps/CascLib/src/common/Array.h ================================================ /*****************************************************************************/ /* Array.h Copyright (c) Ladislav Zezula 2015 */ /*---------------------------------------------------------------------------*/ /* Common array implementation */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 30.10.15 1.00 Lad The first version of DynamicArray.h */ /* 10.08.18 1.00 Lad CLASS-ified, renamed to Array.h */ /*****************************************************************************/ #ifndef __CASC_ARRAY_H__ #define __CASC_ARRAY_H__ //----------------------------------------------------------------------------- // Structures class CASC_ARRAY { public: CASC_ARRAY() { m_pItemArray = NULL; m_ItemCountMax = 0; m_ItemCount = 0; m_ItemSize = 0; } ~CASC_ARRAY() { Free(); } // Creates an array with a custom element type template int Create(size_t ItemCountMax) { return Create(sizeof(TYPE), ItemCountMax); } // Creates an array with a custom element size int Create(size_t ItemSize, size_t ItemCountMax) { // Create the array if ((m_pItemArray = CASC_ALLOC(ItemSize * ItemCountMax)) == NULL) return ERROR_NOT_ENOUGH_MEMORY; m_ItemCountMax = ItemCountMax; m_ItemCount = 0; m_ItemSize = ItemSize; return ERROR_SUCCESS; } // Inserts one or more items; returns pointer to the first inserted item void * Insert(size_t NewItemCount, bool bEnlargeAllowed = true) { void * pNewItems; // Try to enlarge the buffer, if needed if (!EnlargeArray(m_ItemCount + NewItemCount, bEnlargeAllowed)) return NULL; pNewItems = m_pItemArray + (m_ItemCount * m_ItemSize); // Increment the size of the array m_ItemCount += NewItemCount; // Return pointer to the new item return pNewItems; } // Inserts one or more items; returns pointer to the first inserted item void * Insert(const void * NewItems, size_t NewItemCount, bool bEnlargeAllowed = true) { void * pNewItem = Insert(NewItemCount, bEnlargeAllowed); // Copy the item(s) to the array, if any if (pNewItem && NewItems) memcpy(pNewItem, NewItems, (NewItemCount * m_ItemSize)); return pNewItem; } // Returns an item at a given index void * ItemAt(size_t ItemIndex) { return (ItemIndex < m_ItemCount) ? (m_pItemArray + (ItemIndex * m_ItemSize)) : NULL; } void * LastItem() { return m_pItemArray + (m_ItemCount * m_ItemSize); } // Inserts an item at a given index. If there is not enough items in the array, // the array will be enlarged. Should any gaps to be created, the function will zero them void * InsertAt(size_t ItemIndex) { LPBYTE pbLastItem; LPBYTE pbNewItem; // Make sure we have array large enough if(!EnlargeArray(ItemIndex + 1, true)) return NULL; // Get the items range pbLastItem = m_pItemArray + (m_ItemCount * m_ItemSize); pbNewItem = m_pItemArray + (ItemIndex * m_ItemSize); m_ItemCount = CASCLIB_MAX(m_ItemCount, ItemIndex+1); // If we inserted an item past the current end, we need to clear the items in-between if (pbNewItem > pbLastItem) { memset(pbLastItem, 0, (pbNewItem - pbLastItem)); m_ItemCount = ItemIndex + 1; } return pbNewItem; } // Returns index of an item size_t IndexOf(const void * pItem) { LPBYTE pbItem = (LPBYTE)pItem; assert((m_pItemArray <= pbItem) && (pbItem <= m_pItemArray + (m_ItemCount * m_ItemSize))); assert(((pbItem - m_pItemArray) % m_ItemSize) == 0); return ((pbItem - m_pItemArray) / m_ItemSize); } void * ItemArray() { return m_pItemArray; } size_t ItemCount() { return m_ItemCount; } size_t ItemCountMax() { return m_ItemCountMax; } size_t ItemSize() { return m_ItemSize; } bool IsInitialized() { return (m_pItemArray && m_ItemCountMax); } // Invalidates the entire array, but keeps memory allocated void Reset() { memset(m_pItemArray, 0, m_ItemCountMax * m_ItemSize); m_ItemCount = 0; } // Frees the array void Free() { CASC_FREE(m_pItemArray); m_ItemCountMax = m_ItemCount = m_ItemSize = 0; } protected: bool EnlargeArray(size_t NewItemCount, bool bEnlargeAllowed) { LPBYTE NewItemArray; size_t ItemCountMax; // We expect the array to be already allocated assert(m_pItemArray != NULL); assert(m_ItemCountMax != 0); // Shall we enlarge the table? if (NewItemCount > m_ItemCountMax) { // Deny enlarge if not allowed if(bEnlargeAllowed == false) return false; // Calculate new table size ItemCountMax = m_ItemCountMax; while (ItemCountMax < NewItemCount) ItemCountMax = ItemCountMax << 1; // Allocate new table NewItemArray = CASC_REALLOC(BYTE, m_pItemArray, (ItemCountMax * m_ItemSize)); if (NewItemArray == NULL) return false; // Set the new table size m_ItemCountMax = ItemCountMax; m_pItemArray = NewItemArray; } return true; } LPBYTE m_pItemArray; // Pointer to item array size_t m_ItemCountMax; // Maximum item count size_t m_ItemCount; // Current item count size_t m_ItemSize; // Size of an item }; #endif // __CASC_ARRAY__ ================================================ FILE: deps/CascLib/src/common/Common.cpp ================================================ /*****************************************************************************/ /* CascCommon.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Common functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad The first version of CascCommon.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" //----------------------------------------------------------------------------- // Conversion to uppercase/lowercase // Converts ASCII characters to lowercase // Converts backslash (0x5C) to normal slash (0x2F) unsigned char AsciiToLowerTable_Slash[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x2F, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; // Converts ASCII characters to uppercase // Converts slash (0x2F) to backslash (0x5C) unsigned char AsciiToUpperTable_BkSlash[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; // Converts ASCII characters to hexa digit unsigned char AsciiToHexTable[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; unsigned char IntToHexChar[] = "0123456789abcdef"; //----------------------------------------------------------------------------- // GetCascError/SetCascError support for non-Windows platform static DWORD dwLastError = ERROR_SUCCESS; DWORD GetCascError() { #ifdef CASCLIB_PLATFORM_WINDOWS return GetLastError(); #else return dwLastError; #endif } void SetCascError(DWORD dwErrCode) { #ifdef CASCLIB_PLATFORM_WINDOWS SetLastError(dwErrCode); #endif dwLastError = dwErrCode; } //----------------------------------------------------------------------------- // Linear data stream manipulation LPBYTE CaptureInteger16_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue) { // Is there enough data? if((pbDataPtr + sizeof(USHORT)) > pbDataEnd) return NULL; // Convert data from Little endian to PtrValue[0] = ConvertBytesToInteger_2(pbDataPtr); // Return the pointer to data following after the integer return pbDataPtr + sizeof(USHORT); } LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue) { // Is there enough data? if((pbDataPtr + sizeof(DWORD)) > pbDataEnd) return NULL; // Give data PtrValue[0] = *(PDWORD)pbDataPtr; // Return the pointer to data following after the integer return pbDataPtr + sizeof(DWORD); } LPBYTE CaptureInteger32_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue) { // Is there enough data? if((pbDataPtr + sizeof(DWORD)) > pbDataEnd) return NULL; // Convert data from Little endian to PtrValue[0] = ConvertBytesToInteger_4(pbDataPtr); // Return the pointer to data following after the integer return pbDataPtr + sizeof(DWORD); } LPBYTE CaptureByteArray(LPBYTE pbDataPtr, LPBYTE pbDataEnd, size_t nLength, LPBYTE pbOutput) { // Is there enough data? if((pbDataPtr + nLength) > pbDataEnd) return NULL; // Give data memcpy(pbOutput, pbDataPtr, nLength); // Return the pointer to data following after the integer return pbDataPtr + nLength; } LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrCKey) { // Is there enough data? if((pbDataPtr + sizeof(CONTENT_KEY)) > pbDataEnd) return NULL; // Give data PtrCKey[0] = (PCONTENT_KEY)pbDataPtr; // Return the pointer to data following after the integer return pbDataPtr + sizeof(CONTENT_KEY); } LPBYTE CaptureEncodedKey(LPBYTE pbEKey, LPBYTE pbData, BYTE EKeyLength) { // Two usual lengths of EKey assert(EKeyLength == 0x09 || EKeyLength == 0x10); // Copy the first 0x09 bytes if(EKeyLength >= 0x09) { pbEKey[0x00] = pbData[0x00]; pbEKey[0x01] = pbData[0x01]; pbEKey[0x02] = pbData[0x02]; pbEKey[0x03] = pbData[0x03]; pbEKey[0x04] = pbData[0x04]; pbEKey[0x05] = pbData[0x05]; pbEKey[0x06] = pbData[0x06]; pbEKey[0x07] = pbData[0x07]; pbEKey[0x08] = pbData[0x08]; if(EKeyLength == 0x10) { pbEKey[0x09] = pbData[0x09]; pbEKey[0x0A] = pbData[0x0A]; pbEKey[0x0B] = pbData[0x0B]; pbEKey[0x0C] = pbData[0x0C]; pbEKey[0x0D] = pbData[0x0D]; pbEKey[0x0E] = pbData[0x0E]; pbEKey[0x0F] = pbData[0x0F]; } else { pbEKey[0x09] = 0; pbEKey[0x0A] = 0; pbEKey[0x0B] = 0; pbEKey[0x0C] = 0; pbEKey[0x0D] = 0; pbEKey[0x0E] = 0; pbEKey[0x0F] = 0; } } return pbData + EKeyLength; } LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount) { size_t ArraySize = ItemSize * ItemCount; // Is there enough data? if((pbDataPtr + ArraySize) > pbDataEnd) return NULL; // Give data PtrArray[0] = pbDataPtr; // Return the pointer to data following after the array return pbDataPtr + ArraySize; } //----------------------------------------------------------------------------- // String copying and conversion void CascStrCopy(char * szTarget, size_t cchTarget, const char * szSource, size_t cchSource) { size_t cchToCopy; if (cchTarget > 0) { // Make sure we know the length if (cchSource == -1) cchSource = strlen(szSource); cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource); // Copy the string memcpy(szTarget, szSource, cchToCopy); szTarget[cchToCopy] = 0; } } void CascStrCopy(char * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource) { size_t cchToCopy; if (cchTarget > 0) { // Make sure we know the length if (cchSource == -1) cchSource = wcslen(szSource); cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource); wcstombs(szTarget, szSource, cchToCopy); szTarget[cchToCopy] = 0; } } void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const char * szSource, size_t cchSource) { size_t cchToCopy; if (cchTarget > 0) { // Make sure we know the length if (cchSource == -1) cchSource = strlen(szSource); cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource); mbstowcs(szTarget, szSource, cchToCopy); szTarget[cchToCopy] = 0; } } void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource) { size_t cchToCopy; if (cchTarget > 0) { // Make sure we know the length if (cchSource == -1) cchSource = wcslen(szSource); cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource); memcpy(szTarget, szSource, cchToCopy * sizeof(wchar_t)); szTarget[cchToCopy] = 0; } } //----------------------------------------------------------------------------- // Safe version of s(w)printf size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...) { char * buffend; va_list argList; // Start the argument list va_start(argList, format); #ifdef CASCLIB_PLATFORM_WINDOWS StringCchVPrintfExA(buffer, nCount, &buffend, NULL, 0, format, argList); // buffend = buffer + vsnprintf(buffer, nCount, format, argList); #else buffend = buffer + vsnprintf(buffer, nCount, format, argList); #endif // End the argument list va_end(argList); return (buffend - buffer); } size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...) { wchar_t * buffend; va_list argList; // Start the argument list va_start(argList, format); #ifdef CASCLIB_PLATFORM_WINDOWS StringCchVPrintfExW(buffer, nCount, &buffend, NULL, 0, format, argList); // buffend = buffer + vswprintf(buffer, nCount, format, argList); #else buffend = buffer + vswprintf(buffer, nCount, format, argList); #endif // End the argument list va_end(argList); return (buffend - buffer); } //----------------------------------------------------------------------------- // String allocation char * CascNewStr(const char * szString, size_t nCharsToReserve) { char * szNewString = NULL; size_t nLength; if(szString != NULL) { nLength = strlen(szString); szNewString = CASC_ALLOC(nLength + nCharsToReserve + 1); if(szNewString != NULL) { memcpy(szNewString, szString, nLength); szNewString[nLength] = 0; } } return szNewString; } wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve) { wchar_t * szNewString = NULL; size_t nLength; if(szString != NULL) { nLength = wcslen(szString); szNewString = CASC_ALLOC(nLength + nCharsToReserve + 1); if(szNewString != NULL) { memcpy(szNewString, szString, nLength * sizeof(wchar_t)); szNewString[nLength] = 0; } } return szNewString; } LPSTR CascNewStrT2A(LPCTSTR szString, size_t nCharsToReserve) { LPSTR szNewString = NULL; size_t nLength; if(szString != NULL) { nLength = _tcslen(szString); szNewString = CASC_ALLOC(nLength + nCharsToReserve + 1); if(szNewString != NULL) { CascStrCopy(szNewString, nLength + nCharsToReserve + 1, szString, nLength); // szNewString[nLength] = 0; } } return szNewString; } LPTSTR CascNewStrA2T(LPCSTR szString, size_t nCharsToReserve) { LPTSTR szNewString = NULL; size_t nLength; if(szString != NULL) { nLength = strlen(szString); szNewString = CASC_ALLOC(nLength + nCharsToReserve + 1); if(szNewString != NULL) { CascStrCopy(szNewString, nLength + nCharsToReserve + 1, szString, nLength); // szNewString[nLength] = 0; } } return szNewString; } //----------------------------------------------------------------------------- // String merging LPTSTR GetLastPathPart(LPTSTR szWorkPath) { size_t nLength = _tcslen(szWorkPath); // Go one character back if(nLength > 0) nLength--; // Cut ending (back)slashes, if any while(nLength > 0 && (szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/'))) nLength--; // Cut the last path part while(nLength > 0) { // End of path? if(szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/')) { return szWorkPath + nLength; } // Go one character back nLength--; } return NULL; } bool CutLastPathPart(LPTSTR szWorkPath) { // Get the last part of the path szWorkPath = GetLastPathPart(szWorkPath); if(szWorkPath == NULL) return false; szWorkPath[0] = 0; return true; } size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, va_list argList) { CASC_PATH Path(PATH_SEP_CHAR); LPCTSTR szFragment; bool bWithSeparator = false; // Combine all parts of the path here while((szFragment = va_arg(argList, LPTSTR)) != NULL) { Path.AppendString(szFragment, bWithSeparator); bWithSeparator = true; } return Path.Copy(szBuffer, nMaxChars); } size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, ...) { va_list argList; size_t nLength; va_start(argList, nMaxChars); nLength = CombinePath(szBuffer, nMaxChars, argList); va_end(argList); return nLength; } size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars) { char * szNormNameEnd = szNormName + cchMaxChars; size_t i; // Normalize the file name: ToLower + BackSlashToSlash for(i = 0; szFileName[0] != 0 && szNormName < szNormNameEnd; i++) *szNormName++ = NormTable[*szFileName++]; // Terminate the string szNormName[0] = 0; return i; } size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars) { return NormalizeFileName(AsciiToUpperTable_BkSlash, szNormName, szFileName, cchMaxChars); } size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars) { return NormalizeFileName(AsciiToLowerTable_Slash, szNormName, szFileName, cchMaxChars); } ULONGLONG CalcNormNameHash(const char * szNormName, size_t nLength) { uint32_t dwHashHigh = 0; uint32_t dwHashLow = 0; // Calculate the HASH value of the normalized file name hashlittle2(szNormName, nLength, &dwHashHigh, &dwHashLow); return ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow; } ULONGLONG CalcFileNameHash(const char * szFileName) { char szNormName[MAX_PATH+1]; size_t nLength; // Normalize the file name - convert to uppercase, slashes to backslashes nLength = NormalizeFileName_UpperBkSlash(szNormName, szFileName, MAX_PATH); // Calculate hash from the normalized name return CalcNormNameHash(szNormName, nLength); } //----------------------------------------------------------------------------- // File name utilities bool IsFileDataIdName(const char * szFileName, DWORD & FileDataId) { const char * szFilePtr; BYTE BinaryValue[4]; // If the file name begins with "File", then a decimal file data ID must follow if(!strncmp(szFileName, "File", 4)) { DWORD Accumulator = 0; for(szFilePtr = szFileName + 4; szFilePtr[0] != 0 && szFilePtr[0] != '.'; szFilePtr++) { if(!('0' <= szFilePtr[0] && szFilePtr[0] <= '9')) return false; Accumulator = (Accumulator * 10) + (szFilePtr[0] - '0'); } if(szFilePtr[0] == '.' || szFilePtr[0] == 0) { FileDataId = Accumulator; return true; } } // If the file name begins with "FILE", then a hexadecimal file data ID must follow if(!strncmp(szFileName, "FILE", 4) && strlen(szFileName) >= 0x0C) { // Convert the hexadecimal number to integer if(BinaryFromString(szFileName+4, 8, BinaryValue) == ERROR_SUCCESS) { // Must be followed by an extension or end-of-string if(szFileName[0x0C] == 0 || szFileName[0x0C] == '.') { FileDataId = ConvertBytesToInteger_4(BinaryValue); return (FileDataId != CASC_INVALID_ID); } } } return false; } bool IsFileCKeyEKeyName(const char * szFileName, LPBYTE PtrKeyBuffer) { size_t nLength = strlen(szFileName); if(nLength == MD5_STRING_SIZE) { if(BinaryFromString(szFileName, MD5_STRING_SIZE, PtrKeyBuffer) == ERROR_SUCCESS) { return true; } } return false; } bool CascCheckWildCard(const char * szString, const char * szWildCard) { const char * szWildCardPtr; while(szWildCard && szWildCard[0]) { // If there is '?' in the wildcard, we skip one char while(szWildCard[0] == '?') { if(szString[0] == 0) return false; szWildCard++; szString++; } // Handle '*' szWildCardPtr = szWildCard; if(szWildCardPtr[0] != 0) { if(szWildCardPtr[0] == '*') { while(szWildCardPtr[0] == '*') szWildCardPtr++; if(szWildCardPtr[0] == 0) return true; if(AsciiToUpperTable_BkSlash[szWildCardPtr[0]] == AsciiToUpperTable_BkSlash[szString[0]]) { if(CascCheckWildCard(szString, szWildCardPtr)) return true; } } else { if(AsciiToUpperTable_BkSlash[szWildCardPtr[0]] != AsciiToUpperTable_BkSlash[szString[0]]) return false; szWildCard = szWildCardPtr + 1; } if(szString[0] == 0) return false; szString++; } else { return (szString[0] == 0) ? true : false; } } return true; } //----------------------------------------------------------------------------- // Hashing functions bool CascIsValidMD5(LPBYTE pbMd5) { PDWORD Int32Array = (PDWORD)pbMd5; // The MD5 is considered invalid if it is zeroed return (Int32Array[0] | Int32Array[1] | Int32Array[2] | Int32Array[3]) ? true : false; } bool CascVerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5) { MD5_CTX md5_ctx; BYTE md5_digest[MD5_HASH_SIZE]; // Don't verify the block if the MD5 is not valid. if(!CascIsValidMD5(expected_md5)) return true; // Calculate the MD5 of the data block MD5_Init(&md5_ctx); MD5_Update(&md5_ctx, pvDataBlock, cbDataBlock); MD5_Final(md5_digest, &md5_ctx); // Does the MD5's match? return (memcmp(md5_digest, expected_md5, MD5_HASH_SIZE) == 0); } void CascCalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash) { MD5_CTX md5_ctx; MD5_Init(&md5_ctx); MD5_Update(&md5_ctx, pvDataBlock, cbDataBlock); MD5_Final(md5_hash, &md5_ctx); } ================================================ FILE: deps/CascLib/src/common/Common.h ================================================ /*****************************************************************************/ /* CascCommon.h Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Common functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad The first version of CascCommon.h */ /*****************************************************************************/ #ifndef __COMMON_H__ #define __COMMON_H__ //----------------------------------------------------------------------------- // Common macros // Macro for building 64-bit file offset from two 32-bit #define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo) #ifndef ALIGN_TO_SIZE #define ALIGN_TO_SIZE(x, a) (((x) + (a)-1) & ~((a)-1)) #endif // Prevent problems with CRT "min" and "max" functions, // as they are not defined on all platforms #define CASCLIB_MIN(a, b) ((a < b) ? a : b) #define CASCLIB_MAX(a, b) ((a > b) ? a : b) #define CASCLIB_UNUSED(p) ((void)(p)) //----------------------------------------------------------------------------- // Common structures // Structure for static content key (CKey) and encoded key (EKey) // The CKey is a MD5 hash of the file data. // The EKey is (shortened) MD5 hash of the file header, which contains MD5 hashes of all the logical blocks of the file. typedef struct _CONTENT_KEY { BYTE Value[MD5_HASH_SIZE]; // MD5 of the file } CONTENT_KEY, *PCONTENT_KEY, ENCODED_KEY, *PENCODED_KEY; //----------------------------------------------------------------------------- // EKey entry, captured from index files of all types. This structure // is somewhat less memory consuming than CASC_CKEY_ENTRY typedef struct _CASC_EKEY_ENTRY { BYTE EKey[MD5_HASH_SIZE]; // Encoded key. Length depends on TCascStorage::EKeyLength ULONGLONG StorageOffset; // Offset of the encoded file in archive. // Lower (TCascStorage::FileOffsetBits) bits are archive offset. // Upper bits are archive index DWORD EncodedSize; // Encoded size DWORD Alignment; // Alignment to 8-byte boundary. Reserved for future use } CASC_EKEY_ENTRY, *PCASC_EKEY_ENTRY; //----------------------------------------------------------------------------- // Basic structure used by all CascLib objects to describe a single entry // in the CASC storage. Each entry represents one physical file // in the storage. Note that the file may be present under several file names. // Flags for CASC_CKEY_ENTRY::Flags #define CASC_CE_FILE_IS_LOCAL 0x00000001 // The file is available locally. Keep this flag to have value of 1 #define CASC_CE_HAS_CKEY 0x00000002 // The CKey is present in the entry #define CASC_CE_HAS_EKEY 0x00000004 // The EKey is present, at least partial one #define CASC_CE_HAS_EKEY_PARTIAL 0x00000008 // The EKey is only partial, padded by zeros. Always used with CASC_CE_HAS_EKEY #define CASC_CE_IN_ENCODING 0x00000010 // Present in the ENCODING manifest #define CASC_CE_IN_DOWNLOAD 0x00000020 // Present in the DOWNLOAD manifest #define CASC_CE_IN_BUILD 0x00000040 // Present in the BUILD (text) manifest #define CASC_CE_IN_ARCHIVE 0x00000080 // File is stored in an archive (for online storages) #define CASC_CE_FOLDER_ENTRY 0x00000100 // This CKey entry is a folder #define CASC_CE_FILE_SPAN 0x00000200 // This CKey entry is a follow-up file span #define CASC_CE_FILE_PATCH 0x00000400 // The file is in PATCH subfolder in remote storage #define CASC_CE_PLAIN_DATA 0x00000800 // The file data is not BLTE encoded, but in plain format // In-memory representation of a single entry. struct CASC_CKEY_ENTRY { CASC_CKEY_ENTRY() { Init(); } void Init(void) { memset(this, 0, sizeof(CASC_CKEY_ENTRY)); StorageOffset = CASC_INVALID_OFFS64; EncodedSize = CASC_INVALID_SIZE; ContentSize = CASC_INVALID_SIZE; SpanCount = 1; } bool IsFile() { // Must not be a folder entry if((Flags & CASC_CE_FOLDER_ENTRY) == 0) { // There can be entries that are both file span or the standalone file // * zone/zm_red.xpak - { zone/zm_red.xpak_1, zone/zm_red.xpak_2, ..., zone/zm_red.xpak_6 } if(RefCount != 0) return true; // To include the file, it must either be present in ENCODING, DOWNLOAD or in BUILD file if(((Flags & CASC_CE_FILE_SPAN) == 0) && (Flags & (CASC_CE_IN_ENCODING | CASC_CE_IN_DOWNLOAD | CASC_CE_IN_BUILD))) return true; } return false; } BYTE CKey[MD5_HASH_SIZE]; // Content key of the full length BYTE EKey[MD5_HASH_SIZE]; // Encoded key of the full length ULONGLONG StorageOffset; // Linear offset over the entire storage. 0 if not present ULONGLONG TagBitMask; // Bitmap for the tags. 0 ig tags are not supported DWORD ContentSize; // Content size of the file DWORD EncodedSize; // Encoded size of the file DWORD Flags; // See CASC_CE_XXX USHORT RefCount; // This is the number of file names referencing this entry BYTE SpanCount; // Number of spans for the file BYTE Priority; // Download priority }; typedef CASC_CKEY_ENTRY *PCASC_CKEY_ENTRY; //----------------------------------------------------------------------------- // Conversion tables extern unsigned char AsciiToLowerTable_Slash[256]; extern unsigned char AsciiToUpperTable_BkSlash[256]; extern unsigned char AsciiToHexTable[0x80]; extern unsigned char IntToHexChar[]; //----------------------------------------------------------------------------- // Memory management // // We use our own macros for allocating/freeing memory. If you want // to redefine them, please keep the following rules: // // - The memory allocation must return NULL if not enough memory // (i.e not to throw exception) // - The allocating function does not need to fill the allocated buffer with zeros // - The reallocating function must support NULL as the previous block // - Memory freeing function must check for NULL pointer and do nothing if so // #define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type)) template T * CASC_ALLOC(size_t nCount) { return (T *)malloc(nCount * sizeof(T)); } template T * CASC_ALLOC_ZERO(size_t nCount) { T * ptr = CASC_ALLOC(nCount); if(ptr != NULL) memset(ptr, 0, sizeof(T) * nCount); return ptr; } template void CASC_FREE(T *& ptr) { if (ptr != NULL) free(ptr); ptr = NULL; } //----------------------------------------------------------------------------- // 32-bit ROL inline DWORD Rol32(DWORD dwValue, DWORD dwRolCount) { return (dwValue << dwRolCount) | (dwValue >> (32 - dwRolCount)); } //----------------------------------------------------------------------------- // Big endian number manipulation inline DWORD ConvertBytesToInteger_2(LPBYTE ValueAsBytes) { USHORT Value = 0; Value = (Value << 0x08) | ValueAsBytes[0]; Value = (Value << 0x08) | ValueAsBytes[1]; return Value; } inline DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes) { DWORD Value = 0; Value = (Value << 0x08) | ValueAsBytes[0]; Value = (Value << 0x08) | ValueAsBytes[1]; Value = (Value << 0x08) | ValueAsBytes[2]; return Value; } inline DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes) { DWORD Value = 0; Value = (Value << 0x08) | ValueAsBytes[0]; Value = (Value << 0x08) | ValueAsBytes[1]; Value = (Value << 0x08) | ValueAsBytes[2]; Value = (Value << 0x08) | ValueAsBytes[3]; return Value; } // Converts the variable-size big-endian into integer inline DWORD ConvertBytesToInteger_X(LPBYTE ValueAsBytes, DWORD dwByteSize) { DWORD Value = 0; if(dwByteSize > 0) Value = (Value << 0x08) | ValueAsBytes[0]; if(dwByteSize > 1) Value = (Value << 0x08) | ValueAsBytes[1]; if(dwByteSize > 2) Value = (Value << 0x08) | ValueAsBytes[2]; if(dwByteSize > 3) Value = (Value << 0x08) | ValueAsBytes[3]; return Value; } inline DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes) { DWORD Value = 0; Value = (Value << 0x08) | ValueAsBytes[3]; Value = (Value << 0x08) | ValueAsBytes[2]; Value = (Value << 0x08) | ValueAsBytes[1]; Value = (Value << 0x08) | ValueAsBytes[0]; return Value; } // Read the 40-bit big-endian offset into ULONGLONG inline ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes) { ULONGLONG Value = 0; Value = (Value << 0x08) | ValueAsBytes[0]; Value = (Value << 0x08) | ValueAsBytes[1]; Value = (Value << 0x08) | ValueAsBytes[2]; Value = (Value << 0x08) | ValueAsBytes[3]; Value = (Value << 0x08) | ValueAsBytes[4]; return Value; } inline void ConvertIntegerToBytes_4(DWORD Value, LPBYTE ValueAsBytes) { ValueAsBytes[0] = (BYTE)((Value >> 0x18) & 0xFF); ValueAsBytes[1] = (BYTE)((Value >> 0x10) & 0xFF); ValueAsBytes[2] = (BYTE)((Value >> 0x08) & 0xFF); ValueAsBytes[3] = (BYTE)((Value >> 0x00) & 0xFF); } inline void ConvertIntegerToBytes_4_LE(DWORD Value, LPBYTE ValueAsBytes) { ValueAsBytes[0] = (BYTE)((Value >> 0x00) & 0xFF); ValueAsBytes[1] = (BYTE)((Value >> 0x08) & 0xFF); ValueAsBytes[2] = (BYTE)((Value >> 0x10) & 0xFF); ValueAsBytes[3] = (BYTE)((Value >> 0x18) & 0xFF); } // Faster than memset(Buffer, 0, 0x10) inline void ZeroMemory16(void * Buffer) { PDWORD PtrBuffer = (PDWORD)Buffer; PtrBuffer[0] = 0; PtrBuffer[1] = 0; PtrBuffer[2] = 0; PtrBuffer[3] = 0; } // Faster than memcpy(Target, Source, 0x10) inline void CopyMemory16(void * Target, void * Source) { PDWORD PtrTarget = (PDWORD)Target; PDWORD PtrSource = (PDWORD)Source; PtrTarget[0] = PtrSource[0]; PtrTarget[1] = PtrSource[1]; PtrTarget[2] = PtrSource[2]; PtrTarget[3] = PtrSource[3]; } //----------------------------------------------------------------------------- // Capturing various integral values LPBYTE CaptureInteger16_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue); LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue); LPBYTE CaptureInteger32_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue); LPBYTE CaptureByteArray(LPBYTE pbDataPtr, LPBYTE pbDataEnd, size_t nLength, LPBYTE pbOutput); LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrCKey); LPBYTE CaptureEncodedKey(LPBYTE pbEKey, LPBYTE pbData, BYTE EKeyLength); LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount); #define CaptureArray(pbDataPtr, pbDataEnd, PtrArray, type, count) CaptureArray_(pbDataPtr, pbDataEnd, PtrArray, sizeof(type), count) //----------------------------------------------------------------------------- // String copying and conversion void CascStrCopy(char * szTarget, size_t cchTarget, const char * szSource, size_t cchSource = -1); void CascStrCopy(char * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource = -1); void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const char * szSource, size_t cchSource = -1); void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource = -1); //----------------------------------------------------------------------------- // Safe version of s(w)printf size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...); size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...); //----------------------------------------------------------------------------- // String allocation char * CascNewStr(const char * szString, size_t nCharsToReserve = 0); wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve = 0); LPSTR CascNewStrT2A(LPCTSTR szString, size_t nCharsToReserve = 0); LPTSTR CascNewStrA2T(LPCSTR szString, size_t nCharsToReserve = 0); size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, va_list argList); size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, ...); LPTSTR GetLastPathPart(LPTSTR szWorkPath); bool CutLastPathPart(LPTSTR szWorkPath); size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars); size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars); ULONGLONG CalcNormNameHash(const char * szNormName, size_t nLength); ULONGLONG CalcFileNameHash(const char * szFileName); //----------------------------------------------------------------------------- // String conversion functions template DWORD ConvertStringToInt(const xchar * szString, size_t nMaxDigits, INTXX & RefValue, const xchar ** PtrStringEnd = NULL) { INTXX MaxValueMask = (INTXX)0x0F << ((sizeof(INTXX) * 8) - 4); INTXX Accumulator = 0; BYTE DigitOne; // Set default value if(nMaxDigits == 0) nMaxDigits = sizeof(INTXX) * 2; // Convert the string up to the number of digits for(size_t i = 0; i < nMaxDigits; i++, szString++) { // Check for the end of the string if(szString[0] > sizeof(AsciiToHexTable)) return ERROR_BAD_FORMAT; if(szString[0] <= 0x20) break; // Extract the next digit DigitOne = AsciiToHexTable[szString[0]]; if(DigitOne == 0xFF) return ERROR_BAD_FORMAT; // Check overflow. If OK, shift the value by 4 to the left if(Accumulator & MaxValueMask) return ERROR_ARITHMETIC_OVERFLOW; Accumulator = (Accumulator << 4) | DigitOne; } // Give the results if(PtrStringEnd != NULL) PtrStringEnd[0] = szString; RefValue = Accumulator; return ERROR_SUCCESS; } // Converts string blob to binary blob template DWORD BinaryFromString(const xchar * szString, size_t nMaxDigits, LPBYTE pbBinary) { const xchar * szStringEnd = szString + nMaxDigits; DWORD dwCounter = 0; BYTE DigitValue; BYTE ByteValue = 0; // Convert the string while(szString < szStringEnd) { // Retrieve the digit converted to hexa DigitValue = (BYTE)(AsciiToUpperTable_BkSlash[szString[0]] - '0'); if(DigitValue > 9) DigitValue -= 'A' - '9' - 1; if(DigitValue > 0x0F) return ERROR_BAD_FORMAT; // Insert the digit to the binary buffer ByteValue = (ByteValue << 0x04) | DigitValue; dwCounter++; // If we reached the second digit, it means that we need // to flush the byte value and move on if((dwCounter & 0x01) == 0) *pbBinary++ = ByteValue; szString++; } return ERROR_SUCCESS; } // Converts binary array to string. // The caller must ensure that the buffer has at least ((cbBinary * 2) + 1) characters template xchar * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, xchar * szBuffer) { xchar * szSaveBuffer = szBuffer; // Verify the binary pointer if(pbBinary && cbBinary) { // Convert the bytes to string array for(size_t i = 0; i < cbBinary; i++) { *szBuffer++ = IntToHexChar[pbBinary[i] >> 0x04]; *szBuffer++ = IntToHexChar[pbBinary[i] & 0x0F]; } } // Terminate the string *szBuffer = 0; return szSaveBuffer; } //----------------------------------------------------------------------------- // Structures for data blobs struct QUERY_KEY { QUERY_KEY() { pbData = NULL; cbData = 0; } ~QUERY_KEY() { CASC_FREE(pbData); cbData = 0; } DWORD SetData(const void * pv, size_t cb) { if((pbData = CASC_ALLOC(cb)) == NULL) return ERROR_NOT_ENOUGH_MEMORY; memcpy(pbData, pv, cb); cbData = cb; return ERROR_SUCCESS; } LPBYTE pbData; size_t cbData; }; typedef QUERY_KEY *PQUERY_KEY; //----------------------------------------------------------------------------- // File name utilities // Retrieves the pointer to plain name template const XCHAR * GetPlainFileName(const XCHAR * szFileName) { const XCHAR * szPlainName = szFileName; while(*szFileName != 0) { if(*szFileName == '\\' || *szFileName == '/') szPlainName = szFileName + 1; szFileName++; } return szPlainName; } // Retrieves the pointer to file extension template const XCHAR * GetFileExtension(const XCHAR * szFileName) { const XCHAR * szExtension = NULL; // We need to start searching from the plain name // Avoid: C:\$RECYCLE.BIN\File.ext szFileName = GetPlainFileName(szFileName); // Find the last dot in the plain file name while(szFileName[0] != 0) { if(szFileName[0] == '.') szExtension = szFileName; szFileName++; } // If not found, return the end of the file name return (XCHAR *)((szExtension != NULL) ? szExtension : szFileName); } bool IsFileDataIdName(const char * szFileName, DWORD & FileDataId); bool IsFileCKeyEKeyName(const char * szFileName, LPBYTE PtrKeyBuffer); bool CascCheckWildCard(const char * szString, const char * szWildCard); //----------------------------------------------------------------------------- // Hashing functions ULONGLONG HashStringJenkins(const char * szFileName); bool CascIsValidMD5(LPBYTE pbMd5); void CascCalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash); bool CascVerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5); //----------------------------------------------------------------------------- // Scanning a directory typedef bool (*INDEX_FILE_FOUND)(LPCTSTR szFileName, void * pvContext); bool DirectoryExists(LPCTSTR szDirectory); int ScanIndexDirectory(LPCTSTR szIndexPath, INDEX_FILE_FOUND pfnOnFileFound, void * pvContext); //----------------------------------------------------------------------------- // Argument structure versioning // Safely retrieves field value from a structure // intended for cases where users upgrade CascLib by simply dropping in a new .dll without recompiling their app template bool ExtractVersionedArgument(const ARG_HOLDER * pHolder, size_t ArgOffset, ARG * pArg) { if (pHolder == NULL) return false; // Check input structure size if (ArgOffset + sizeof(ARG) > pHolder->Size) return false; *pArg = *((ARG *)(((char*)pHolder) + ArgOffset)); return true; } #endif // __COMMON_H__ ================================================ FILE: deps/CascLib/src/common/Csv.cpp ================================================ /*****************************************************************************/ /* Csv.cpp Copyright (c) Ladislav Zezula 2019 */ /*---------------------------------------------------------------------------*/ /* Implementation for the CSV handler class */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 24.05.19 1.00 Lad Created */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" //----------------------------------------------------------------------------- // Local variables static const CASC_CSV_LINE NullLine; #define NullColumn NullLine.Columns[0]; //----------------------------------------------------------------------------- // Local functions static char * NextLine_Default(void * /* pvUserData */, char * szLine) { // Find the end of the line while(szLine[0] != 0 && szLine[0] != 0x0A && szLine[0] != 0x0D) szLine++; // Terminate the line while(szLine[0] == 0x0A || szLine[0] == 0x0D) *szLine++ = 0; // If there's an end-of-string, it's over return (szLine[0] != 0) ? szLine : NULL; } static char * NextColumn_Default(void * /* pvUserData */, char * szColumn) { // Find the end of the column while(szColumn[0] != 0 && szColumn[0] != '|') szColumn++; // Terminate the column if (szColumn[0] == '|') { *szColumn++ = 0; return szColumn; } // If there's an end-of-string, it's over return NULL; } static size_t CalcHashValue(const char * szString) { size_t dwHash = 0x7EEE7EEE; // Hash the string itself while(szString[0] != 0) { dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[0]; szString++; } // Return the hash limited by the table size return dwHash; } static BYTE HashToIndex(size_t dwHashValue) { return (BYTE)(dwHashValue & (CSV_HASH_TABLE_SIZE - 1)); } //----------------------------------------------------------------------------- // Class for CSV line CASC_CSV_LINE::CASC_CSV_LINE() { m_pParent = NULL; m_nColumns = 0; } CASC_CSV_LINE::~CASC_CSV_LINE() {} bool CASC_CSV_LINE::SetLine(CASC_CSV * pParent, char * szCsvLine) { CASC_CSV_NEXTPROC PfnNextColumn = pParent->GetNextColumnProc(); char * szCsvColumn; size_t nHdrColumns = 0; size_t nColumns = 0; // Reset the number of column to zero m_pParent = pParent; m_nColumns = 0; // Parse each column while(szCsvLine != NULL && nColumns < CSV_MAX_COLUMNS) { // Get current line and the next one szCsvColumn = szCsvLine; szCsvLine = PfnNextColumn(pParent->GetUserData(), szCsvLine); // Save the current line Columns[nColumns].szValue = szCsvColumn; Columns[nColumns].nLength = strlen(szCsvColumn); nColumns++; } // Columns overflow if(nColumns >= CSV_MAX_COLUMNS) { assert(false); return false; } // If the parent has header lines, then the number of columns must match // In the case of mismatched column count, ignore the line nHdrColumns = pParent->GetHeaderColumns(); if(nHdrColumns != 0 && nColumns != nHdrColumns) return false; // All OK m_nColumns = nColumns; return true; } const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](const char * szColumnName) const { size_t nIndex; // The column must have a hash table if(m_pParent != NULL) { nIndex = m_pParent->GetColumnIndex(szColumnName); if(nIndex != CSV_INVALID_INDEX && nIndex < m_nColumns) { return Columns[nIndex]; } } return NullColumn; } const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](size_t nIndex) const { return (nIndex < m_nColumns) ? Columns[nIndex] : NullColumn; } //----------------------------------------------------------------------------- // Class for CSV object CASC_CSV::CASC_CSV(size_t nLinesMax, bool bHasHeader) { // Initialize the class variables memset(HashTable, 0xFF, sizeof(HashTable)); m_pvUserData = NULL; m_szCsvFile = NULL; m_szCsvPtr = NULL; m_nCsvFile = 0; m_nLines = 0; m_bHasHeader = bHasHeader; // Initialize the "NextLine" function that will serve for finding next line in the text PfnNextLine = NextLine_Default; PfnNextColumn = NextColumn_Default; // Nonzero number of lines means a finite CSV handler. The CSV will be loaded at once. // Zero means that the user needs to call LoadNextLine() and then the line data if(nLinesMax != 0) { m_pLines = new CASC_CSV_LINE[nLinesMax]; m_nLinesMax = nLinesMax; m_bHasAllLines = true; } else { m_pLines = new CASC_CSV_LINE[1]; m_nLinesMax = 1; m_bHasAllLines = false; } } CASC_CSV::~CASC_CSV() { if(m_pLines != NULL) delete[] m_pLines; CASC_FREE(m_szCsvFile); } DWORD CASC_CSV::SetNextLineProc(CASC_CSV_NEXTPROC PfnNextLineProc, CASC_CSV_NEXTPROC PfnNextColProc, void * pvUserData) { // Set the procedure for next line parsing if(PfnNextLineProc != NULL) PfnNextLine = PfnNextLineProc; // Set procedure for next columne parsing if(PfnNextColProc != NULL) PfnNextColumn = PfnNextColProc; // Save the user data m_pvUserData = pvUserData; return ERROR_SUCCESS; } DWORD CASC_CSV::Load(LPCTSTR szFileName) { DWORD cbFileData = 0; DWORD dwErrCode = ERROR_SUCCESS; m_szCsvFile = (char *)LoadFileToMemory(szFileName, &cbFileData); if (m_szCsvFile != NULL) { // Assign the data to the CSV object m_szCsvPtr = m_szCsvFile; m_nCsvFile = cbFileData; // Parse the data dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } else { dwErrCode = GetCascError(); } return dwErrCode; } DWORD CASC_CSV::Load(LPBYTE pbData, size_t cbData) { DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; m_szCsvFile = CASC_ALLOC(cbData + 1); if (m_szCsvFile != NULL) { // Copy the entire data and terminate them with zero memcpy(m_szCsvFile, pbData, cbData); m_szCsvFile[cbData] = 0; m_szCsvPtr = m_szCsvFile; m_nCsvFile = cbData; // Parse the data dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } return dwErrCode; } bool CASC_CSV::LoadNextLine() { bool bResult = false; // Only endless CSV handler can do this if(m_bHasAllLines == false) { // A few checks assert(m_pLines != NULL); assert(m_nLinesMax == 1); // Reset the current line and load it bResult = LoadNextLine(m_pLines[0]); m_nLines = (bResult) ? 1 : 0; } return bResult; } bool CASC_CSV::InitializeHashTable() { // Create the hash table of HeaderText -> ColumnIndex for(size_t i = 0; i < Header.GetColumnCount(); i++) { // Calculate the start slot and the current slot size_t nStartIndex = HashToIndex(CalcHashValue(Header[i].szValue)); size_t nHashIndex = nStartIndex; // Go as long as there is not a free space while(HashTable[nHashIndex] != 0xFF) { nHashIndex = HashToIndex(nHashIndex + 1); } // Set the slot HashTable[nHashIndex] = (BYTE)i; } return true; } bool CASC_CSV::LoadNextLine(CASC_CSV_LINE & Line) { // Overwatch ROOT's header begins with "#" if(m_szCsvPtr == m_szCsvFile && m_szCsvPtr[0] == '#') m_szCsvPtr++; // Parse the entire line while(m_szCsvPtr != NULL && m_szCsvPtr[0] != 0) { char * szCsvLine = m_szCsvPtr; // Get the next line m_szCsvPtr = PfnNextLine(m_pvUserData, m_szCsvPtr); // Initialize the line if (Line.SetLine(this, szCsvLine)) return true; } // End-of-file found return false; } bool CASC_CSV::ParseCsvData() { // Sanity checks assert(m_nLines == 0); // If we have header, then parse it and set the pointer to the next line if(m_bHasHeader) { // Load the current line to the header if (!LoadNextLine(Header)) return false; // Initialize the hash table if(!InitializeHashTable()) return false; } // Are we supposed to load all lines? if(m_bHasAllLines) { // Parse each line for(size_t i = 0; i < m_nLinesMax; i++) { if(!LoadNextLine(m_pLines[i])) break; m_nLines++; } } return true; } const CASC_CSV_COLUMN & CASC_CSV::operator[](const char * szColumnName) const { if (m_pLines == NULL || m_nLines == 0) return NullColumn; return m_pLines[0][GetColumnIndex(szColumnName)]; } const CASC_CSV_LINE & CASC_CSV::operator[](size_t nIndex) const { if (m_pLines == NULL || nIndex >= m_nLines) return NullLine; return m_pLines[nIndex]; } size_t CASC_CSV::GetHeaderColumns() const { return (m_bHasHeader) ? Header.GetColumnCount() : 0; } size_t CASC_CSV::GetColumnIndex(const char * szColumnName) const { if(m_bHasHeader) { // Calculate the start slot and the current slot size_t nStartIndex = HashToIndex(CalcHashValue(szColumnName)); size_t nHashIndex = nStartIndex; size_t nIndex; // Go as long as there is not a free space while((nIndex = HashTable[nHashIndex]) != 0xFF) { // Compare the string if(!strcmp(Header[nIndex].szValue, szColumnName)) return nIndex; // Move to the next column nHashIndex = HashToIndex(nHashIndex + 1); } } return CSV_INVALID_INDEX; } ================================================ FILE: deps/CascLib/src/common/Csv.h ================================================ /*****************************************************************************/ /* Csv.h Copyright (c) Ladislav Zezula 2019 */ /*---------------------------------------------------------------------------*/ /* Interface for the CSV handler class */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 24.05.19 1.00 Lad Created */ /*****************************************************************************/ #ifndef __CSV_H__ #define __CSV_H__ //----------------------------------------------------------------------------- // Defines #define CSV_INVALID_INDEX ((size_t)(-1)) #define CSV_ZERO ((size_t)(0)) // Use Csv[0][CSV_ZERO] instead of ambiguous Csv[0][0] #define CSV_MAX_COLUMNS 0x20 #define CSV_HASH_TABLE_SIZE 0x80 //----------------------------------------------------------------------------- // Interface for finding of next text element (line, column) // The function must find the next (line|column), put zero there and return begin // of the next (line|column). In case there is no next (line|column), the function returns NULL typedef char * (*CASC_CSV_NEXTPROC)(void * pvUserData, char * szLine); //----------------------------------------------------------------------------- // Class for CSV line class CASC_CSV; struct CASC_CSV_COLUMN { CASC_CSV_COLUMN() { szValue = NULL; nLength = 0; } const char * szValue; size_t nLength; }; class CASC_CSV_LINE { public: CASC_CSV_LINE(); ~CASC_CSV_LINE(); bool SetLine(CASC_CSV * pParent, char * szLine); const CASC_CSV_COLUMN & operator[](const char * szColumnName) const; const CASC_CSV_COLUMN & operator[](size_t nIndex) const; size_t GetColumnCount() const { return m_nColumns; } protected: friend class CASC_CSV; CASC_CSV * m_pParent; CASC_CSV_COLUMN Columns[CSV_MAX_COLUMNS]; size_t m_nColumns; }; class CASC_CSV { public: CASC_CSV(size_t nLinesMax, bool bHasHeader); ~CASC_CSV(); DWORD SetNextLineProc(CASC_CSV_NEXTPROC PfnNextLineProc, CASC_CSV_NEXTPROC PfnNextColProc = NULL, void * pvUserData = NULL); CASC_CSV_NEXTPROC GetNextColumnProc() { return PfnNextColumn; } DWORD Load(LPBYTE pbData, size_t cbData); DWORD Load(LPCTSTR szFileName); bool LoadNextLine(); const CASC_CSV_COLUMN & operator[](const char * szColumnName) const; const CASC_CSV_LINE & operator[](size_t nIndex) const; size_t GetHeaderColumns() const; size_t GetColumnIndex(const char * szColumnName) const; void * GetUserData() const { return m_pvUserData; } size_t GetLineCount() const { return m_nLines; } protected: bool InitializeHashTable(); bool LoadNextLine(CASC_CSV_LINE & Line); bool ParseCsvData(); CASC_CSV_NEXTPROC PfnNextLine; CASC_CSV_NEXTPROC PfnNextColumn; CASC_CSV_LINE * m_pLines; CASC_CSV_LINE Header; BYTE HashTable[CSV_HASH_TABLE_SIZE]; void * m_pvUserData; char * m_szCsvFile; char * m_szCsvPtr; size_t m_nCsvFile; size_t m_nLinesMax; size_t m_nLines; bool m_bHasHeader; bool m_bHasAllLines; }; #endif // __CSV_H__ ================================================ FILE: deps/CascLib/src/common/Directory.cpp ================================================ /*****************************************************************************/ /* Directory.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* System-dependent directory functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad The first version of Directory.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" //----------------------------------------------------------------------------- // Public functions bool DirectoryExists(LPCTSTR szDirectory) { #ifdef CASCLIB_PLATFORM_WINDOWS DWORD dwAttributes = GetFileAttributes(szDirectory); if((dwAttributes != INVALID_FILE_ATTRIBUTES) && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) return true; #else // CASCLIB_PLATFORM_WINDOWS DIR * dir = opendir(szDirectory); if(dir != NULL) { closedir(dir); return true; } #endif return false; } bool MakeDirectory(LPCTSTR szDirectory) { #ifdef CASCLIB_PLATFORM_WINDOWS BOOL bResult = CreateDirectory(szDirectory, NULL); return (bResult) ? true : false; #else return (mkdir(szDirectory, 0755) == 0); #endif } int ScanIndexDirectory( LPCTSTR szIndexPath, INDEX_FILE_FOUND pfnOnFileFound, void * pvContext) { #ifdef CASCLIB_PLATFORM_WINDOWS WIN32_FIND_DATA wf; HANDLE hFind; TCHAR szSearchMask[MAX_PATH]; // Prepare the search mask CombinePath(szSearchMask, _countof(szSearchMask), szIndexPath, _T("*"), NULL); // Prepare directory search hFind = FindFirstFile(szSearchMask, &wf); if(hFind != INVALID_HANDLE_VALUE) { // Skip the first file as it's always just "." or ".." while(FindNextFile(hFind, &wf)) { // If the found object is a file, pass it to the handler if(!(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { // Let the callback scan the file name pfnOnFileFound(wf.cFileName, pvContext); } } // Close the search handle FindClose(hFind); } #else // CASCLIB_PLATFORM_WINDOWS struct dirent * dir_entry; DIR * dir; dir = opendir(szIndexPath); if(dir != NULL) { while((dir_entry = readdir(dir)) != NULL) { if(dir_entry->d_type != DT_DIR) { pfnOnFileFound(dir_entry->d_name, pvContext); } } closedir(dir); } #endif return ERROR_SUCCESS; } ================================================ FILE: deps/CascLib/src/common/Directory.h ================================================ /*****************************************************************************/ /* Directory.h Copyright (c) Ladislav Zezula 2015 */ /*---------------------------------------------------------------------------*/ /* Directory functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 30.10.15 1.00 Lad The first version of Directory.h */ /*****************************************************************************/ #ifndef __DIRECTORY_H__ #define __DIRECTORY_H__ //----------------------------------------------------------------------------- // Scanning a directory bool DirectoryExists(LPCTSTR szDirectory); bool MakeDirectory(LPCTSTR szDirectory); int ScanIndexDirectory( LPCTSTR szIndexPath, INDEX_FILE_FOUND pfnOnFileFound, void * pvContext ); #endif // __DIRECTORY_H__ ================================================ FILE: deps/CascLib/src/common/FileStream.cpp ================================================ /*****************************************************************************/ /* FileStream.cpp Copyright (c) Ladislav Zezula 2010 */ /*---------------------------------------------------------------------------*/ /* File stream support */ /* */ /* Windows support: Written by Ladislav Zezula */ /* Mac support: Written by Sam Wilkins */ /* Linux support: Written by Sam Wilkins and Ivan Komissarov */ /* Big-endian: Written & debugged by Sam Wilkins */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 28.04.14 1.00 Lad Copied from StormLib */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" #ifdef _MSC_VER #pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) #endif //----------------------------------------------------------------------------- // Local functions - platform-specific functions static ULONGLONG GetByteOffset(ULONGLONG * ByteOffset1, ULONGLONG ByteOffset2) { return (ByteOffset1 != NULL) ? ByteOffset1[0] : ByteOffset2; } static DWORD StringToInt(const char * szString) { DWORD dwValue = 0; while('0' <= szString[0] && szString[0] <= '9') { dwValue = (dwValue * 10) + (szString[0] - '9'); szString++; } return dwValue; } //----------------------------------------------------------------------------- // Dummy init function static void BaseNone_Init(TFileStream *) { // Nothing here } //----------------------------------------------------------------------------- // Local functions - base file support static bool BaseFile_Create(TFileStream * pStream) { #ifdef CASCLIB_PLATFORM_WINDOWS { DWORD dwWriteShare = (pStream->dwFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; pStream->Base.File.hFile = CreateFile(pStream->szFileName, GENERIC_READ | GENERIC_WRITE, dwWriteShare | FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) return false; } #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) { intptr_t handle; handle = open(pStream->szFileName, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if(handle == -1) { pStream->Base.File.hFile = INVALID_HANDLE_VALUE; SetCascError(errno); return false; } pStream->Base.File.hFile = (HANDLE)handle; } #endif // Reset the file size and position pStream->Base.File.FileSize = 0; pStream->Base.File.FilePos = 0; return true; } static bool BaseFile_Open(TFileStream * pStream, LPCTSTR szFileName, DWORD dwStreamFlags) { #ifdef CASCLIB_PLATFORM_WINDOWS { ULARGE_INTEGER FileSize; DWORD dwWriteAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES; DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; // Open the file pStream->Base.File.hFile = CreateFile(szFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES | dwWriteAccess, FILE_SHARE_READ | dwWriteShare, NULL, OPEN_EXISTING, 0, NULL); if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) return false; // Query the file size FileSize.LowPart = GetFileSize(pStream->Base.File.hFile, &FileSize.HighPart); pStream->Base.File.FileSize = FileSize.QuadPart; // Query last write time GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->Base.File.FileTime); } #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) { struct stat64 fileinfo; int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; intptr_t handle; // Open the file pStream->Base.File.hFile = INVALID_HANDLE_VALUE; handle = open(szFileName, oflag | O_LARGEFILE); if(handle == -1) { SetCascError(errno); return false; } // Get the file size if(fstat64(handle, &fileinfo) == -1) { SetCascError(errno); close(handle); return false; } // time_t is number of seconds since 1.1.1970, UTC. // 1 second = 10000000 (decimal) in FILETIME // Set the start to 1.1.1970 00:00:00 pStream->Base.File.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime); pStream->Base.File.FileSize = (ULONGLONG)fileinfo.st_size; pStream->Base.File.hFile = (HANDLE)handle; } #endif // Reset the file position pStream->Base.File.FilePos = 0; return true; } static bool BaseFile_Read( TFileStream * pStream, // Pointer to an open stream ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position void * pvBuffer, // Pointer to data to be read DWORD dwBytesToRead) // Number of bytes to read from the file { DWORD dwBytesRead = 0; // Must be set by platform-specific code // Synchronize the access to the TFileStream structure CascLock(pStream->Lock); { ULONGLONG ByteOffset = GetByteOffset(pByteOffset, pStream->Base.File.FilePos); #ifdef CASCLIB_PLATFORM_WINDOWS { // Note: We no longer support Windows 9x. // Thus, we can use the OVERLAPPED structure to specify // file offset to read from file. This allows us to skip // one system call to SetFilePointer // Update the byte offset pStream->Base.File.FilePos = ByteOffset; // Read the data if (dwBytesToRead != 0) { OVERLAPPED Overlapped; Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32); Overlapped.Offset = (DWORD)ByteOffset; Overlapped.hEvent = NULL; if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, &Overlapped)) { CascUnlock(pStream->Lock); return false; } } } #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) { ssize_t bytes_read; // If the byte offset is different from the current file position, // we have to update the file position if (ByteOffset != pStream->Base.File.FilePos) { if (lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET) == (off64_t)-1) { CascUnlock(pStream->Lock); SetCascError(errno); return false; } pStream->Base.File.FilePos = ByteOffset; } // Perform the read operation if (dwBytesToRead != 0) { bytes_read = read((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToRead); if (bytes_read == -1) { CascUnlock(pStream->Lock); SetCascError(errno); return false; } dwBytesRead = (DWORD)(size_t)bytes_read; } } #endif // Increment the current file position by number of bytes read pStream->Base.File.FilePos = ByteOffset + dwBytesRead; } CascUnlock(pStream->Lock); // If the number of bytes read doesn't match to required amount, return false // However, Blizzard's CASC handlers read encoded data so that if less than expected // was read, then they fill the rest with zeros if(dwBytesRead < dwBytesToRead) { if(pStream->dwFlags & STREAM_FLAG_FILL_MISSING) { memset((LPBYTE)pvBuffer + dwBytesRead, 0, (dwBytesToRead - dwBytesRead)); dwBytesRead = dwBytesToRead; } else { SetCascError(ERROR_HANDLE_EOF); } } return (dwBytesRead == dwBytesToRead); } /** * \a pStream Pointer to an open stream * \a pByteOffset Pointer to file byte offset. If NULL, writes to current position * \a pvBuffer Pointer to data to be written * \a dwBytesToWrite Number of bytes to write to the file */ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) { DWORD dwBytesWritten = 0; // Must be set by platform-specific code // Synchronize the access to the TFileStream structure CascLock(pStream->Lock); { ULONGLONG ByteOffset = GetByteOffset(pByteOffset, pStream->Base.File.FilePos); #ifdef CASCLIB_PLATFORM_WINDOWS { // Note: We no longer support Windows 9x. // Thus, we can use the OVERLAPPED structure to specify // file offset to read from file. This allows us to skip // one system call to SetFilePointer // Update the byte offset pStream->Base.File.FilePos = ByteOffset; // Read the data if (dwBytesToWrite != 0) { OVERLAPPED Overlapped; Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32); Overlapped.Offset = (DWORD)ByteOffset; Overlapped.hEvent = NULL; if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, &Overlapped)) { CascUnlock(pStream->Lock); return false; } } } #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) { ssize_t bytes_written; // If the byte offset is different from the current file position, // we have to update the file position if (ByteOffset != pStream->Base.File.FilePos) { if (lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET) == (off64_t)-1) { CascUnlock(pStream->Lock); SetCascError(errno); return false; } pStream->Base.File.FilePos = ByteOffset; } // Perform the read operation bytes_written = write((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToWrite); if (bytes_written == -1) { CascUnlock(pStream->Lock); SetCascError(errno); return false; } dwBytesWritten = (DWORD)(size_t)bytes_written; } #endif // Increment the current file position by number of bytes read pStream->Base.File.FilePos = ByteOffset + dwBytesWritten; // Also modify the file size, if needed if(pStream->Base.File.FilePos > pStream->Base.File.FileSize) pStream->Base.File.FileSize = pStream->Base.File.FilePos; } CascUnlock(pStream->Lock); if(dwBytesWritten != dwBytesToWrite) SetCascError(ERROR_DISK_FULL); return (dwBytesWritten == dwBytesToWrite); } /** * \a pStream Pointer to an open stream * \a NewFileSize New size of the file */ static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize) { #ifdef CASCLIB_PLATFORM_WINDOWS { LONG FileSizeHi = (LONG)(NewFileSize >> 32); LONG FileSizeLo; DWORD dwNewPos; bool bResult; // Set the position at the new file size dwNewPos = SetFilePointer(pStream->Base.File.hFile, (LONG)NewFileSize, &FileSizeHi, FILE_BEGIN); if(dwNewPos == INVALID_SET_FILE_POINTER && GetCascError() != ERROR_SUCCESS) return false; // Set the current file pointer as the end of the file bResult = (bool)SetEndOfFile(pStream->Base.File.hFile); if(bResult) pStream->Base.File.FileSize = NewFileSize; // Restore the file position FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32); FileSizeLo = (LONG)(pStream->Base.File.FilePos); SetFilePointer(pStream->Base.File.hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN); return bResult; } #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) { if(ftruncate64((intptr_t)pStream->Base.File.hFile, (off64_t)NewFileSize) == -1) { SetCascError(errno); return false; } pStream->Base.File.FileSize = NewFileSize; return true; } #endif } // Gives the current file size static bool BaseFile_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) { // Note: Used by all thre base providers. // Requires the TBaseData union to have the same layout for all three base providers *pFileSize = pStream->Base.File.FileSize; return true; } // Gives the current file position static bool BaseFile_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) { // Note: Used by all thre base providers. // Requires the TBaseData union to have the same layout for all three base providers *pByteOffset = pStream->Base.File.FilePos; return true; } // Renames the file pointed by pStream so that it contains data from pNewStream static bool BaseFile_Replace(TFileStream * pStream, TFileStream * pNewStream) { #ifdef CASCLIB_PLATFORM_WINDOWS // Rename the new file to the old stream's file return (bool)MoveFileEx(pNewStream->szFileName, pStream->szFileName, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) // "rename" on Linux also works if the target file exists if(rename(pNewStream->szFileName, pStream->szFileName) == -1) { SetCascError(errno); return false; } return true; #endif } static void BaseFile_Close(TFileStream * pStream) { // Synchronize the access to multiple threads CascLock(pStream->Lock); { if(pStream->Base.File.hFile != INVALID_HANDLE_VALUE) { #ifdef CASCLIB_PLATFORM_WINDOWS CloseHandle(pStream->Base.File.hFile); #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) close((intptr_t)pStream->Base.File.hFile); #endif } // Also invalidate the handle pStream->Base.File.hFile = INVALID_HANDLE_VALUE; } CascUnlock(pStream->Lock); } // Initializes base functions for the disk file static void BaseFile_Init(TFileStream * pStream) { pStream->BaseCreate = BaseFile_Create; pStream->BaseOpen = BaseFile_Open; pStream->BaseRead = BaseFile_Read; pStream->BaseWrite = BaseFile_Write; pStream->BaseResize = BaseFile_Resize; pStream->BaseGetSize = BaseFile_GetSize; pStream->BaseGetPos = BaseFile_GetPos; pStream->BaseClose = BaseFile_Close; } //----------------------------------------------------------------------------- // Local functions - base memory-mapped file support static bool BaseMap_Open(TFileStream * pStream, LPCTSTR szFileName, DWORD dwStreamFlags) { #ifdef CASCLIB_PLATFORM_WINDOWS ULARGE_INTEGER FileSize; HANDLE hFile; HANDLE hMap; bool bResult = false; // Keep compiler happy dwStreamFlags = dwStreamFlags; // Open the file for read access hFile = CreateFile(szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if(hFile != INVALID_HANDLE_VALUE) { // Retrieve file size. Don't allow mapping file of a zero size. FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); if(FileSize.QuadPart != 0) { // Now create mapping object hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if(hMap != NULL) { // Map the entire view into memory // Note that this operation will fail if the file can't fit // into usermode address space pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); if(pStream->Base.Map.pbFile != NULL) { // Retrieve file time GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime); // Retrieve file size and position pStream->Base.Map.FileSize = FileSize.QuadPart; pStream->Base.Map.FilePos = 0; bResult = true; } // Close the map handle CloseHandle(hMap); } } // Close the file handle CloseHandle(hFile); } // If the file is not there and is not available for random access, // report error if(bResult == false) return false; #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) struct stat64 fileinfo; intptr_t handle; bool bResult = false; // Open the file handle = open(szFileName, O_RDONLY); if(handle != -1) { // Get the file size if(fstat64(handle, &fileinfo) != -1) { pStream->Base.Map.pbFile = (LPBYTE)mmap(NULL, (size_t)fileinfo.st_size, PROT_READ, MAP_PRIVATE, handle, 0); if(pStream->Base.Map.pbFile != NULL) { // time_t is number of seconds since 1.1.1970, UTC. // 1 second = 10000000 (decimal) in FILETIME // Set the start to 1.1.1970 00:00:00 pStream->Base.Map.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime); pStream->Base.Map.FileSize = (ULONGLONG)fileinfo.st_size; pStream->Base.Map.FilePos = 0; bResult = true; } } close(handle); } // Did the mapping fail? if(bResult == false) { SetCascError(errno); return false; } #endif return true; } static bool BaseMap_Read( TFileStream * pStream, // Pointer to an open stream ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position void * pvBuffer, // Pointer to data to be read DWORD dwBytesToRead) // Number of bytes to read from the file { ULONGLONG ByteOffset = GetByteOffset(pByteOffset, pStream->Base.Map.FilePos); // Do we have to read anything at all? if(dwBytesToRead != 0) { // Don't allow reading past file size if((ByteOffset + dwBytesToRead) > pStream->Base.Map.FileSize) return false; // Copy the required data memcpy(pvBuffer, pStream->Base.Map.pbFile + (size_t)ByteOffset, dwBytesToRead); } // Move the current file position pStream->Base.Map.FilePos += dwBytesToRead; return true; } static void BaseMap_Close(TFileStream * pStream) { #ifdef CASCLIB_PLATFORM_WINDOWS if(pStream->Base.Map.pbFile != NULL) UnmapViewOfFile(pStream->Base.Map.pbFile); #endif #if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX) if(pStream->Base.Map.pbFile != NULL) munmap(pStream->Base.Map.pbFile, (size_t )pStream->Base.Map.FileSize); #endif pStream->Base.Map.pbFile = NULL; } // Initializes base functions for the mapped file static void BaseMap_Init(TFileStream * pStream) { // Supply the file stream functions pStream->BaseOpen = BaseMap_Open; pStream->BaseRead = BaseMap_Read; pStream->BaseGetSize = BaseFile_GetSize; // Reuse BaseFile function pStream->BaseGetPos = BaseFile_GetPos; // Reuse BaseFile function pStream->BaseClose = BaseMap_Close; // Mapped files are read-only pStream->dwFlags |= STREAM_FLAG_READ_ONLY; } //----------------------------------------------------------------------------- // Local functions - base HTTP file support static DWORD BaseHttp_ParseURL(TFileStream * pStream, LPCTSTR szFileName) { LPCTSTR szFilePtr = szFileName; char * hostName; char * fileName; // Find the end od the host name if((szFilePtr = _tcschr(szFileName, '/')) == NULL) return ERROR_INVALID_PARAMETER; // Allocate and copy the host name if((hostName = CASC_ALLOC(szFilePtr - szFileName + 1)) != NULL) { CascStrCopy(hostName, 256, szFileName, (szFilePtr - szFileName)); // Allocate and copy the resource name if((fileName = CascNewStrT2A(szFilePtr)) != NULL) { pStream->Base.Socket.hostName = hostName; pStream->Base.Socket.fileName = fileName; return ERROR_SUCCESS; } CASC_FREE(hostName); } return ERROR_NOT_ENOUGH_MEMORY; } static bool BaseHttp_Download(TFileStream * pStream) { CASC_MIME Mime; const char * request_mask = "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n"; char * server_response; char * fileName = pStream->Base.Socket.fileName; char request[0x100]; size_t response_length = 0; size_t request_length = 0; DWORD dwErrCode; // If we already have the data, it's success if(pStream->Base.Socket.fileData == NULL) { // Construct the request, either HTTP or Ribbit (https://wowdev.wiki/Ribbit). // Note that Ribbit requests don't start with slash if((pStream->dwFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_RIBBIT) { if(fileName[0] == '/') fileName++; request_mask = "%s\r\n"; } // Send the request and receive decoded response request_length = CascStrPrintf(request, _countof(request), request_mask, fileName, pStream->Base.Socket.hostName); server_response = pStream->Base.Socket.pSocket->ReadResponse(request, request_length, &response_length); if(server_response != NULL) { // Decode the MIME document if((dwErrCode = Mime.Load(server_response, response_length)) == ERROR_SUCCESS) { // Move the data from MIME to HTTP stream pStream->Base.Socket.fileData = Mime.GiveAway(&pStream->Base.Socket.fileDataLength); } CASC_FREE(server_response); } } // If we have data loaded, return true return (pStream->Base.Socket.fileData != NULL); } static bool BaseHttp_Open(TFileStream * pStream, LPCTSTR szFileName, DWORD dwStreamFlags) { DWORD dwErrCode; // Extract the server part if((dwErrCode = BaseHttp_ParseURL(pStream, szFileName)) == ERROR_SUCCESS) { // Determine the proper port PCASC_SOCKET pSocket; int portNum = ((dwStreamFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_RIBBIT) ? CASC_PORT_RIBBIT : CASC_PORT_HTTP; // Initiate the remote connection if((pSocket = sockets_connect(pStream->Base.Socket.hostName, portNum)) != NULL) { pStream->Base.Socket.pSocket = pSocket; return true; } } // Failure: set the last error and return false SetCascError(dwErrCode); return false; } static bool BaseHttp_Read( TFileStream * pStream, // Pointer to an open stream ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position void * pvBuffer, // Pointer to data to be read DWORD dwBytesToRead) // Number of bytes to read from the file { ULONGLONG ByteOffset = GetByteOffset(pByteOffset, pStream->Base.Socket.fileDataPos); bool bCanReadTheWholeRange = true; // Synchronize the access to the TFileStream structure CascLock(pStream->Lock); { // Do we have to read anything at all? if(dwBytesToRead != 0) { // Make sure that we have the file downloaded if(!BaseHttp_Download(pStream)) { CascUnlock(pStream->Lock); return false; } // Are we trying to read more than available? if(ByteOffset <= pStream->Base.Socket.fileDataLength) { if((ByteOffset + dwBytesToRead) > pStream->Base.Socket.fileDataLength) { bCanReadTheWholeRange = false; dwBytesToRead = (DWORD)(pStream->Base.Socket.fileDataLength - ByteOffset); } } else { bCanReadTheWholeRange = false; dwBytesToRead = 0; } // Copy the data if(dwBytesToRead != 0) { memcpy(pvBuffer, pStream->Base.Socket.fileData + ByteOffset, dwBytesToRead); } } // Increment the current file position by number of bytes read pStream->Base.Socket.fileDataPos = (size_t)(ByteOffset + dwBytesToRead); } CascUnlock(pStream->Lock); // If the number of bytes read doesn't match the required amount, return false if(bCanReadTheWholeRange == false) SetCascError(ERROR_HANDLE_EOF); return bCanReadTheWholeRange; } // Gives the current file size static bool BaseHttp_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) { bool bResult; // Synchronize the access to the TFileStream structure CascLock(pStream->Lock); { // Make sure that we have the file data bResult = BaseHttp_Download(pStream); if(bResult) { *pFileSize = pStream->Base.Socket.fileDataLength; } } CascUnlock(pStream->Lock); // Give the result return bResult; } static bool BaseHttp_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) { // Give the current position *pByteOffset = pStream->Base.Socket.fileDataPos; return true; } static void BaseHttp_Close(TFileStream * pStream) { if(pStream->Base.Socket.fileData != NULL) CASC_FREE(pStream->Base.Socket.fileData); if(pStream->Base.Socket.hostName != NULL) CASC_FREE(pStream->Base.Socket.hostName); if(pStream->Base.Socket.fileName != NULL) CASC_FREE(pStream->Base.Socket.fileName); if(pStream->Base.Socket.pSocket != NULL) pStream->Base.Socket.pSocket->Release(); memset(&pStream->Base.Socket, 0, sizeof(pStream->Base.Socket)); } // Initializes base functions for the mapped file static void BaseHttp_Init(TFileStream * pStream) { // Supply the stream functions pStream->BaseOpen = BaseHttp_Open; pStream->BaseRead = BaseHttp_Read; pStream->BaseGetSize = BaseHttp_GetSize; pStream->BaseGetPos = BaseHttp_GetPos; pStream->BaseClose = BaseHttp_Close; // HTTP files are read-only pStream->dwFlags |= STREAM_FLAG_READ_ONLY; } //----------------------------------------------------------------------------- // Local functions - base block-based support // Generic function that loads blocks from the file // The function groups the block with the same availability, // so the called BlockRead can finish the request in a single system call static bool BlockStream_Read( TBlockStream * pStream, // Pointer to an open stream ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position void * pvBuffer, // Pointer to data to be read DWORD dwBytesToRead) // Number of bytes to read from the file { ULONGLONG BlockOffset0; ULONGLONG BlockOffset; ULONGLONG ByteOffset; ULONGLONG EndOffset; LPBYTE TransferBuffer; LPBYTE BlockBuffer; DWORD BlockBufferOffset; // Offset of the desired data in the block buffer DWORD BytesNeeded; // Number of bytes that really need to be read DWORD BlockSize = pStream->BlockSize; DWORD BlockCount; bool bPrevBlockAvailable; bool bCallbackCalled = false; bool bBlockAvailable; bool bResult = true; // The base block read function must be present assert(pStream->BlockRead != NULL); // NOP reading of zero bytes if(dwBytesToRead == 0) return true; // Get the current position in the stream ByteOffset = GetByteOffset(pByteOffset, pStream->StreamPos); EndOffset = ByteOffset + dwBytesToRead; if(EndOffset > pStream->StreamSize) { SetCascError(ERROR_HANDLE_EOF); return false; } // Calculate the block parameters BlockOffset0 = BlockOffset = ByteOffset & ~((ULONGLONG)BlockSize - 1); BlockCount = (DWORD)(((EndOffset - BlockOffset) + (BlockSize - 1)) / BlockSize); BytesNeeded = (DWORD)(EndOffset - BlockOffset); // Remember where we have our data assert((BlockSize & (BlockSize - 1)) == 0); BlockBufferOffset = (DWORD)(ByteOffset & (BlockSize - 1)); // Allocate buffer for reading blocks TransferBuffer = BlockBuffer = CASC_ALLOC(BlockCount * BlockSize); if(TransferBuffer == NULL) { SetCascError(ERROR_NOT_ENOUGH_MEMORY); return false; } // If all blocks are available, just read all blocks at once if(pStream->IsComplete == 0) { // Now parse the blocks and send the block read request // to all blocks with the same availability assert(pStream->BlockCheck != NULL); bPrevBlockAvailable = pStream->BlockCheck(pStream, BlockOffset); // Loop as long as we have something to read while(BlockOffset < EndOffset) { // Determine availability of the next block bBlockAvailable = pStream->BlockCheck(pStream, BlockOffset); // If the availability has changed, read all blocks up to this one if(bBlockAvailable != bPrevBlockAvailable) { // Call the file stream callback, if the block is not available if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false) { pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0)); bCallbackCalled = true; } // Load the continuous blocks with the same availability assert(BlockOffset > BlockOffset0); bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable); if(!bResult) break; // Move the block offset BlockBuffer += (DWORD)(BlockOffset - BlockOffset0); BytesNeeded -= (DWORD)(BlockOffset - BlockOffset0); bPrevBlockAvailable = bBlockAvailable; BlockOffset0 = BlockOffset; } // Move to the block offset in the stream BlockOffset += BlockSize; } // If there is a block(s) remaining to be read, do it if(BlockOffset > BlockOffset0) { // Call the file stream callback, if the block is not available if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false) { pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0)); bCallbackCalled = true; } // Read the complete blocks from the file if(BlockOffset > pStream->StreamSize) BlockOffset = pStream->StreamSize; bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable); } } else { // Read the complete blocks from the file if(EndOffset > pStream->StreamSize) EndOffset = pStream->StreamSize; bResult = pStream->BlockRead(pStream, BlockOffset, EndOffset, BlockBuffer, BytesNeeded, true); } // Now copy the data to the user buffer if(bResult) { memcpy(pvBuffer, TransferBuffer + BlockBufferOffset, dwBytesToRead); pStream->StreamPos = ByteOffset + dwBytesToRead; } else { // If the block read failed, set the last error SetCascError(ERROR_FILE_INCOMPLETE); } // Call the callback to indicate we are done if(bCallbackCalled) pStream->pfnCallback(pStream->UserData, 0, 0); // Free the block buffer and return CASC_FREE(TransferBuffer); return bResult; } static bool BlockStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) { *pFileSize = pStream->StreamSize; return true; } static bool BlockStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) { *pByteOffset = pStream->StreamPos; return true; } static void BlockStream_Close(TBlockStream * pStream) { // Free the data map, if any CASC_FREE(pStream->FileBitmap); // Call the base class for closing the stream pStream->BaseClose(pStream); } //----------------------------------------------------------------------------- // File stream allocation function static STREAM_INIT StreamBaseInit[5] = { BaseFile_Init, BaseMap_Init, BaseHttp_Init, BaseHttp_Init, // Ribbit provider shares code with HTTP provider BaseNone_Init }; // This function allocates an empty structure for the file stream // The stream structure is created as flat block, variable length // The file name is placed after the end of the stream structure data static TFileStream * AllocateFileStream( LPCTSTR szFileName, size_t StreamSize, DWORD dwStreamFlags) { TFileStream * pMaster = NULL; TFileStream * pStream; LPCTSTR szNextFile = szFileName; size_t FileNameSize; // Sanity check assert(StreamSize != 0); // The caller can specify chain of files in the following form: // C:\archive.MPQ*http://www.server.com/MPQs/archive-server.MPQ // In that case, we use the part after "*" as master file name while(szNextFile[0] != 0 && szNextFile[0] != _T('*')) szNextFile++; FileNameSize = (size_t)((szNextFile - szFileName) * sizeof(TCHAR)); // If we have a next file, we need to open it as master stream // Note that we don't care if the master stream exists or not, // If it doesn't, later attempts to read missing file block will fail if(szNextFile[0] == _T('*')) { // Don't allow another master file in the string if(_tcschr(szNextFile + 1, _T('*')) != NULL) { SetCascError(ERROR_INVALID_PARAMETER); return NULL; } // Open the master file pMaster = FileStream_OpenFile(szNextFile + 1, STREAM_FLAG_READ_ONLY); } // Allocate the stream structure for the given stream type pStream = (TFileStream *)CASC_ALLOC(StreamSize + FileNameSize + sizeof(TCHAR)); if(pStream != NULL) { // Zero the entire structure memset(pStream, 0, StreamSize + FileNameSize + sizeof(TCHAR)); pStream->pMaster = pMaster; pStream->dwFlags = dwStreamFlags; // Initialize the file name pStream->szFileName = (LPTSTR)((BYTE *)pStream + StreamSize); memcpy(pStream->szFileName, szFileName, FileNameSize); pStream->szFileName[FileNameSize / sizeof(TCHAR)] = 0; // Initialize the stream lock CascInitLock(pStream->Lock); // Initialize the stream functions StreamBaseInit[dwStreamFlags & 0x03](pStream); } return pStream; } //----------------------------------------------------------------------------- // Local functions - flat stream support static DWORD FlatStream_CheckFile(TBlockStream * pStream) { LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; DWORD WholeByteCount = (pStream->BlockCount / 8); DWORD ExtraBitsCount = (pStream->BlockCount & 7); BYTE ExpectedValue; // Verify the whole bytes - their value must be 0xFF for(DWORD i = 0; i < WholeByteCount; i++) { if(FileBitmap[i] != 0xFF) return 0; } // If there are extra bits, calculate the mask if(ExtraBitsCount != 0) { ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1); if(FileBitmap[WholeByteCount] != ExpectedValue) return 0; } // Yes, the file is complete return 1; } static bool FlatStream_LoadBitmap(TBlockStream * pStream) { FILE_BITMAP_FOOTER Footer; ULONGLONG ByteOffset; LPBYTE FileBitmap; DWORD BlockCount; DWORD BitmapSize; // Do not load the bitmap if we should not have to if(!(pStream->dwFlags & STREAM_FLAG_USE_BITMAP)) return false; // Only if the size is greater than size of bitmap footer if(pStream->Base.File.FileSize > sizeof(FILE_BITMAP_FOOTER)) { // Load the bitmap footer ByteOffset = pStream->Base.File.FileSize - sizeof(FILE_BITMAP_FOOTER); if(pStream->BaseRead(pStream, &ByteOffset, &Footer, sizeof(FILE_BITMAP_FOOTER))) { // Make sure that the array is properly BSWAP-ed BSWAP_ARRAY32_UNSIGNED((PDWORD)(&Footer), sizeof(FILE_BITMAP_FOOTER)); // Verify if there is actually a footer if(Footer.Signature == ID_FILE_BITMAP_FOOTER && Footer.Version == 0x03) { // Get the offset of the bitmap, number of blocks and size of the bitmap ByteOffset = MAKE_OFFSET64(Footer.MapOffsetHi, Footer.MapOffsetLo); BlockCount = (DWORD)(((ByteOffset - 1) / Footer.BlockSize) + 1); BitmapSize = ((BlockCount + 7) / 8); // Check if the sizes match if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->Base.File.FileSize) { // Allocate space for the bitmap FileBitmap = CASC_ALLOC(BitmapSize); if(FileBitmap != NULL) { // Load the bitmap bits if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize)) { CASC_FREE(FileBitmap); return false; } // Update the stream size pStream->BuildNumber = Footer.BuildNumber; pStream->StreamSize = ByteOffset; // Fill the bitmap information pStream->FileBitmap = FileBitmap; pStream->BitmapSize = BitmapSize; pStream->BlockSize = Footer.BlockSize; pStream->BlockCount = BlockCount; pStream->IsComplete = FlatStream_CheckFile(pStream); return true; } } } } } return false; } static void FlatStream_UpdateBitmap( TBlockStream * pStream, // Pointer to an open stream ULONGLONG StartOffset, ULONGLONG EndOffset) { LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; DWORD BlockIndex; DWORD BlockSize = pStream->BlockSize; DWORD ByteIndex; BYTE BitMask; // Sanity checks assert((StartOffset & (BlockSize - 1)) == 0); assert(FileBitmap != NULL); // Calculate the index of the block BlockIndex = (DWORD)(StartOffset / BlockSize); ByteIndex = (BlockIndex / 0x08); BitMask = (BYTE)(1 << (BlockIndex & 0x07)); // Set all bits for the specified range while(StartOffset < EndOffset) { // Set the bit FileBitmap[ByteIndex] |= BitMask; // Move all StartOffset += BlockSize; ByteIndex += (BitMask >> 0x07); BitMask = (BitMask >> 0x07) | (BitMask << 0x01); } // Increment the bitmap update count pStream->IsModified = 1; } static bool FlatStream_BlockCheck( TBlockStream * pStream, // Pointer to an open stream ULONGLONG BlockOffset) { LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; DWORD BlockIndex; BYTE BitMask; // Sanity checks assert((BlockOffset & (pStream->BlockSize - 1)) == 0); assert(FileBitmap != NULL); // Calculate the index of the block BlockIndex = (DWORD)(BlockOffset / pStream->BlockSize); BitMask = (BYTE)(1 << (BlockIndex & 0x07)); // Check if the bit is present return (FileBitmap[BlockIndex / 0x08] & BitMask) ? true : false; } static bool FlatStream_BlockRead( TBlockStream * pStream, // Pointer to an open stream ULONGLONG StartOffset, ULONGLONG EndOffset, LPBYTE BlockBuffer, DWORD BytesNeeded, bool bAvailable) { DWORD BytesToRead = (DWORD)(EndOffset - StartOffset); // The starting offset must be aligned to size of the block assert(pStream->FileBitmap != NULL); assert((StartOffset & (pStream->BlockSize - 1)) == 0); assert(StartOffset < EndOffset); // If the blocks are not available, we need to load them from the master // and then save to the mirror if(bAvailable == false) { // If we have no master, we cannot satisfy read request if(pStream->pMaster == NULL) return false; // Load the blocks from the master stream // Note that we always have to read complete blocks // so they get properly stored to the mirror stream if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead)) return false; // Store the loaded blocks to the mirror file. // Note that this operation is not required to succeed if(pStream->BaseWrite(pStream, &StartOffset, BlockBuffer, BytesToRead)) FlatStream_UpdateBitmap(pStream, StartOffset, EndOffset); return true; } else { if(BytesToRead > BytesNeeded) BytesToRead = BytesNeeded; return pStream->BaseRead(pStream, &StartOffset, BlockBuffer, BytesToRead); } } static void FlatStream_Close(TBlockStream * pStream) { FILE_BITMAP_FOOTER Footer; if(pStream->FileBitmap && pStream->IsModified) { // Write the file bitmap pStream->BaseWrite(pStream, &pStream->StreamSize, pStream->FileBitmap, pStream->BitmapSize); // Prepare and write the file footer Footer.Signature = ID_FILE_BITMAP_FOOTER; Footer.Version = 3; Footer.BuildNumber = pStream->BuildNumber; Footer.MapOffsetLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF); Footer.MapOffsetHi = (DWORD)(pStream->StreamSize >> 0x20); Footer.BlockSize = pStream->BlockSize; BSWAP_ARRAY32_UNSIGNED(&Footer, sizeof(FILE_BITMAP_FOOTER)); pStream->BaseWrite(pStream, NULL, &Footer, sizeof(FILE_BITMAP_FOOTER)); } // Close the base class BlockStream_Close(pStream); } static bool FlatStream_CreateMirror(TBlockStream * pStream) { ULONGLONG MasterSize = 0; ULONGLONG MirrorSize = 0; LPBYTE FileBitmap = NULL; DWORD dwBitmapSize; DWORD dwBlockCount; bool bNeedCreateMirrorStream = true; bool bNeedResizeMirrorStream = true; // Do we have master function and base creation function? if(pStream->pMaster == NULL || pStream->BaseCreate == NULL) return false; // Retrieve the master file size, block count and bitmap size FileStream_GetSize(pStream->pMaster, &MasterSize); dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); dwBitmapSize = (DWORD)((dwBlockCount + 7) / 8); // Setup stream size and position pStream->BuildNumber = DEFAULT_BUILD_NUMBER; // BUGBUG: Really??? pStream->StreamSize = MasterSize; pStream->StreamPos = 0; // Open the base stream for write access if(pStream->BaseOpen(pStream, pStream->szFileName, 0)) { // If the file open succeeded, check if the file size matches required size pStream->BaseGetSize(pStream, &MirrorSize); if(MirrorSize == MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER)) { // Attempt to load an existing file bitmap if(FlatStream_LoadBitmap(pStream)) return true; // We need to create new file bitmap bNeedResizeMirrorStream = false; } // We need to create mirror stream bNeedCreateMirrorStream = false; } // Create a new stream, if needed if(bNeedCreateMirrorStream) { if(!pStream->BaseCreate(pStream)) return false; } // If we need to, then resize the mirror stream if(bNeedResizeMirrorStream) { if(!pStream->BaseResize(pStream, MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER))) return false; } // Allocate the bitmap array FileBitmap = CASC_ALLOC_ZERO(dwBitmapSize); if(FileBitmap == NULL) return false; // Initialize the bitmap pStream->FileBitmap = FileBitmap; pStream->BitmapSize = dwBitmapSize; pStream->BlockSize = DEFAULT_BLOCK_SIZE; pStream->BlockCount = dwBlockCount; pStream->IsComplete = 0; pStream->IsModified = 1; // Note: Don't write the stream bitmap right away. // Doing so would cause sparse file resize on NTFS, // which would take long time on larger files. return true; } static TFileStream * FlatStream_Open(LPCTSTR szFileName, DWORD dwStreamFlags) { TBlockStream * pStream; ULONGLONG ByteOffset = 0; // Create new empty stream pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); if(pStream == NULL) { SetCascError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } // Do we have a master stream? if(pStream->pMaster != NULL) { if(!FlatStream_CreateMirror(pStream)) { FileStream_Close(pStream); SetCascError(ERROR_FILE_NOT_FOUND); return NULL; } } else { // Attempt to open the base stream if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) { FileStream_Close(pStream); return NULL; } // Load the bitmap, if required to if(dwStreamFlags & STREAM_FLAG_USE_BITMAP) FlatStream_LoadBitmap(pStream); } // If we have a stream bitmap, set the reading functions // which check presence of each file block if(pStream->FileBitmap != NULL) { // Set the stream position to zero. Stream size is already set assert(pStream->StreamSize != 0); pStream->StreamPos = 0; pStream->dwFlags |= STREAM_FLAG_READ_ONLY; // Supply the stream functions pStream->StreamRead = (STREAM_READ)BlockStream_Read; pStream->StreamGetSize = BlockStream_GetSize; pStream->StreamGetPos = BlockStream_GetPos; pStream->StreamClose = (STREAM_CLOSE)FlatStream_Close; // Supply the block functions pStream->BlockCheck = (BLOCK_CHECK)FlatStream_BlockCheck; pStream->BlockRead = (BLOCK_READ)FlatStream_BlockRead; } else { // Reset the base position to zero pStream->BaseRead(pStream, &ByteOffset, NULL, 0); // Setup stream size and position pStream->StreamSize = pStream->Base.File.FileSize; pStream->StreamPos = 0; // Set the base functions pStream->StreamRead = pStream->BaseRead; pStream->StreamWrite = pStream->BaseWrite; pStream->StreamResize = pStream->BaseResize; pStream->StreamGetSize = pStream->BaseGetSize; pStream->StreamGetPos = pStream->BaseGetPos; pStream->StreamClose = pStream->BaseClose; } return pStream; } //----------------------------------------------------------------------------- // Local functions - partial stream support static bool IsPartHeader(PPART_FILE_HEADER pPartHdr) { // Version number must be 2 if(pPartHdr->PartialVersion == 2) { // GameBuildNumber must be an ASCII number if(isdigit(pPartHdr->GameBuildNumber[0]) && isdigit(pPartHdr->GameBuildNumber[1]) && isdigit(pPartHdr->GameBuildNumber[2])) { // Block size must be power of 2 if((pPartHdr->BlockSize & (pPartHdr->BlockSize - 1)) == 0) return true; } } return false; } static DWORD PartStream_CheckFile(TBlockStream * pStream) { PPART_FILE_MAP_ENTRY FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap; DWORD dwBlockCount; // Get the number of blocks dwBlockCount = (DWORD)((pStream->StreamSize + pStream->BlockSize - 1) / pStream->BlockSize); // Check all blocks for(DWORD i = 0; i < dwBlockCount; i++, FileBitmap++) { // Few sanity checks assert(FileBitmap->LargeValueHi == 0); assert(FileBitmap->LargeValueLo == 0); assert(FileBitmap->Flags == 0 || FileBitmap->Flags == 3); // Check if this block is present if(FileBitmap->Flags != 3) return 0; } // Yes, the file is complete return 1; } static bool PartStream_LoadBitmap(TBlockStream * pStream) { PPART_FILE_MAP_ENTRY FileBitmap; PART_FILE_HEADER PartHdr; ULONGLONG ByteOffset = 0; ULONGLONG StreamSize = 0; DWORD BlockCount; DWORD BitmapSize; // Only if the size is greater than size of the bitmap header if(pStream->Base.File.FileSize > sizeof(PART_FILE_HEADER)) { // Attempt to read PART file header if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER))) { // We need to swap PART file header on big-endian platforms BSWAP_ARRAY32_UNSIGNED(&PartHdr, sizeof(PART_FILE_HEADER)); // Verify the PART file header if(IsPartHeader(&PartHdr)) { // Get the number of blocks and size of one block StreamSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo); ByteOffset = sizeof(PART_FILE_HEADER); BlockCount = (DWORD)((StreamSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize); BitmapSize = BlockCount * sizeof(PART_FILE_MAP_ENTRY); // Check if sizes match if((ByteOffset + BitmapSize) < pStream->Base.File.FileSize) { // Allocate space for the array of PART_FILE_MAP_ENTRY FileBitmap = CASC_ALLOC(BlockCount); if(FileBitmap != NULL) { // Load the block map if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize)) { CASC_FREE(FileBitmap); return false; } // Make sure that the byte order is correct BSWAP_ARRAY32_UNSIGNED(FileBitmap, BitmapSize); // Update the stream size pStream->BuildNumber = StringToInt(PartHdr.GameBuildNumber); pStream->StreamSize = StreamSize; // Fill the bitmap information pStream->FileBitmap = FileBitmap; pStream->BitmapSize = BitmapSize; pStream->BlockSize = PartHdr.BlockSize; pStream->BlockCount = BlockCount; pStream->IsComplete = PartStream_CheckFile(pStream); return true; } } } } } return false; } static void PartStream_UpdateBitmap( TBlockStream * pStream, // Pointer to an open stream ULONGLONG StartOffset, ULONGLONG EndOffset, ULONGLONG RealOffset) { PPART_FILE_MAP_ENTRY FileBitmap; DWORD BlockSize = pStream->BlockSize; // Sanity checks assert((StartOffset & (BlockSize - 1)) == 0); assert(pStream->FileBitmap != NULL); // Calculate the first entry in the block map FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (StartOffset / BlockSize); // Set all bits for the specified range while(StartOffset < EndOffset) { // Set the bit FileBitmap->BlockOffsHi = (DWORD)(RealOffset >> 0x20); FileBitmap->BlockOffsLo = (DWORD)(RealOffset & 0xFFFFFFFF); FileBitmap->Flags = 3; // Move all StartOffset += BlockSize; RealOffset += BlockSize; FileBitmap++; } // Increment the bitmap update count pStream->IsModified = 1; } static bool PartStream_BlockCheck( TBlockStream * pStream, // Pointer to an open stream ULONGLONG BlockOffset) { PPART_FILE_MAP_ENTRY FileBitmap; // Sanity checks assert((BlockOffset & (pStream->BlockSize - 1)) == 0); assert(pStream->FileBitmap != NULL); // Calculate the block map entry FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (BlockOffset / pStream->BlockSize); // Check if the flags are present return (FileBitmap->Flags & 0x03) ? true : false; } static bool PartStream_BlockRead( TBlockStream * pStream, ULONGLONG StartOffset, ULONGLONG EndOffset, LPBYTE BlockBuffer, DWORD BytesNeeded, bool bAvailable) { PPART_FILE_MAP_ENTRY FileBitmap; ULONGLONG ByteOffset; DWORD BytesToRead; DWORD BlockIndex = (DWORD)(StartOffset / pStream->BlockSize); // The starting offset must be aligned to size of the block assert(pStream->FileBitmap != NULL); assert((StartOffset & (pStream->BlockSize - 1)) == 0); assert(StartOffset < EndOffset); // If the blocks are not available, we need to load them from the master // and then save to the mirror if(bAvailable == false) { // If we have no master, we cannot satisfy read request if(pStream->pMaster == NULL) return false; // Load the blocks from the master stream // Note that we always have to read complete blocks // so they get properly stored to the mirror stream BytesToRead = (DWORD)(EndOffset - StartOffset); if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead)) return false; // The loaded blocks are going to be stored to the end of the file // Note that this operation is not required to succeed if(pStream->BaseGetSize(pStream, &ByteOffset)) { // Store the loaded blocks to the mirror file. if(pStream->BaseWrite(pStream, &ByteOffset, BlockBuffer, BytesToRead)) { PartStream_UpdateBitmap(pStream, StartOffset, EndOffset, ByteOffset); } } } else { // Get the file map entry FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + BlockIndex; // Read all blocks while(StartOffset < EndOffset) { // Get the number of bytes to be read BytesToRead = (DWORD)(EndOffset - StartOffset); if(BytesToRead > pStream->BlockSize) BytesToRead = pStream->BlockSize; if(BytesToRead > BytesNeeded) BytesToRead = BytesNeeded; // Read the block ByteOffset = MAKE_OFFSET64(FileBitmap->BlockOffsHi, FileBitmap->BlockOffsLo); if(!pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead)) return false; // Move the pointers StartOffset += pStream->BlockSize; BlockBuffer += pStream->BlockSize; BytesNeeded -= pStream->BlockSize; FileBitmap++; } } return true; } static void PartStream_Close(TBlockStream * pStream) { PART_FILE_HEADER PartHeader; ULONGLONG ByteOffset = 0; if(pStream->FileBitmap && pStream->IsModified) { // Prepare the part file header memset(&PartHeader, 0, sizeof(PART_FILE_HEADER)); PartHeader.PartialVersion = 2; PartHeader.FileSizeHi = (DWORD)(pStream->StreamSize >> 0x20); PartHeader.FileSizeLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF); PartHeader.BlockSize = pStream->BlockSize; // Make sure that the header is properly BSWAPed BSWAP_ARRAY32_UNSIGNED(&PartHeader, sizeof(PART_FILE_HEADER)); CascStrPrintf(PartHeader.GameBuildNumber, _countof(PartHeader.GameBuildNumber), "%u", (unsigned int)pStream->BuildNumber); // Write the part header pStream->BaseWrite(pStream, &ByteOffset, &PartHeader, sizeof(PART_FILE_HEADER)); // Write the block bitmap BSWAP_ARRAY32_UNSIGNED(pStream->FileBitmap, pStream->BitmapSize); pStream->BaseWrite(pStream, NULL, pStream->FileBitmap, pStream->BitmapSize); } // Close the base class BlockStream_Close(pStream); } static bool PartStream_CreateMirror(TBlockStream * pStream) { ULONGLONG RemainingSize; ULONGLONG MasterSize = 0; ULONGLONG MirrorSize = 0; LPBYTE FileBitmap = NULL; DWORD dwBitmapSize; DWORD dwBlockCount; bool bNeedCreateMirrorStream = true; bool bNeedResizeMirrorStream = true; // Do we have master function and base creation function? if(pStream->pMaster == NULL || pStream->BaseCreate == NULL) return false; // Retrieve the master file size, block count and bitmap size FileStream_GetSize(pStream->pMaster, &MasterSize); dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); dwBitmapSize = (DWORD)(dwBlockCount * sizeof(PART_FILE_MAP_ENTRY)); // Setup stream size and position pStream->BuildNumber = DEFAULT_BUILD_NUMBER; // BUGBUG: Really??? pStream->StreamSize = MasterSize; pStream->StreamPos = 0; // Open the base stream for write access if(pStream->BaseOpen(pStream, pStream->szFileName, 0)) { // If the file open succeeded, check if the file size matches required size pStream->BaseGetSize(pStream, &MirrorSize); if(MirrorSize >= sizeof(PART_FILE_HEADER) + dwBitmapSize) { // Check if the remaining size is aligned to block RemainingSize = MirrorSize - sizeof(PART_FILE_HEADER) - dwBitmapSize; if((RemainingSize & (DEFAULT_BLOCK_SIZE - 1)) == 0 || RemainingSize == MasterSize) { // Attempt to load an existing file bitmap if(PartStream_LoadBitmap(pStream)) return true; } } // We need to create mirror stream bNeedCreateMirrorStream = false; } // Create a new stream, if needed if(bNeedCreateMirrorStream) { if(!pStream->BaseCreate(pStream)) return false; } // If we need to, then resize the mirror stream if(bNeedResizeMirrorStream) { if(!pStream->BaseResize(pStream, sizeof(PART_FILE_HEADER) + dwBitmapSize)) return false; } // Allocate the bitmap array FileBitmap = CASC_ALLOC_ZERO(dwBitmapSize); if(FileBitmap == NULL) return false; // Initialize the bitmap pStream->FileBitmap = FileBitmap; pStream->BitmapSize = dwBitmapSize; pStream->BlockSize = DEFAULT_BLOCK_SIZE; pStream->BlockCount = dwBlockCount; pStream->IsComplete = 0; pStream->IsModified = 1; // Note: Don't write the stream bitmap right away. // Doing so would cause sparse file resize on NTFS, // which would take long time on larger files. return true; } static TFileStream * PartStream_Open(LPCTSTR szFileName, DWORD dwStreamFlags) { TBlockStream * pStream; // Create new empty stream pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); if(pStream == NULL) return NULL; // Do we have a master stream? if(pStream->pMaster != NULL) { if(!PartStream_CreateMirror(pStream)) { FileStream_Close(pStream); SetCascError(ERROR_FILE_NOT_FOUND); return NULL; } } else { // Attempt to open the base stream if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) { FileStream_Close(pStream); return NULL; } // Load the part stream block map if(!PartStream_LoadBitmap(pStream)) { FileStream_Close(pStream); SetCascError(ERROR_BAD_FORMAT); return NULL; } } // Set the stream position to zero. Stream size is already set assert(pStream->StreamSize != 0); pStream->StreamPos = 0; pStream->dwFlags |= STREAM_FLAG_READ_ONLY; // Set new function pointers pStream->StreamRead = (STREAM_READ)BlockStream_Read; pStream->StreamGetPos = BlockStream_GetPos; pStream->StreamGetSize = BlockStream_GetSize; pStream->StreamClose = (STREAM_CLOSE)PartStream_Close; // Supply the block functions pStream->BlockCheck = (BLOCK_CHECK)PartStream_BlockCheck; pStream->BlockRead = (BLOCK_READ)PartStream_BlockRead; return pStream; } //----------------------------------------------------------------------------- // Local functions - encrypted stream support static const char * szKeyTemplate = "expand 32-byte k000000000000000000000000000000000000000000000000"; static const char * AuthCodeArray[] = { // Starcraft II (Heart of the Swarm) // Authentication code URL: http://dist.blizzard.com/mediakey/hots-authenticationcode-bgdl.txt // -0C- -1C--08- -18--04- -14--00- -10- "S48B6CDTN5XEQAKQDJNDLJBJ73FDFM3U", // SC2 Heart of the Swarm-all : "expand 32-byte kQAKQ0000FM3UN5XE000073FD6CDT0000LJBJS48B0000DJND" // Diablo III: Agent.exe (1.0.0.954) // Address of decryption routine: 00502b00 // Pointer to decryptor object: ECX // Pointer to key: ECX+0x5C // Authentication code URL: http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt // -0C- -1C--08- -18--04- -14--00- -10- "UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK", // Diablo III Installer (deDE): "expand 32-byte kEFH40000QRZKY3520000XC9MF6EJ0000CFH2UCMX0000XFRX" "MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP", // Diablo III Installer (enGB): "expand 32-byte kXP4G0000PHBPRP7W0000J9UNHY4800007SL9MMKV0000HYBQ" "8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP", // Diablo III Installer (enSG): "expand 32-byte kTZ9M00003CPPVGGL0000JYETWHQ70000FDCL8MXL0000QZQS" "EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ", // Diablo III Installer (enUS): "expand 32-byte kGUNG0000WZSZXFE20000UAKP5TM60000HKQ9EJ2R00005QDG" "PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT", // Diablo III Installer (esES): "expand 32-byte kK65U0000HQQTZ6LN0000CLP4BE420000WZVMPBGF0000GJQ3" "X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2", // Diablo III Installer (esMX): "expand 32-byte kW5P200008VU2TSGC0000JPEYJJS90000C47AX7SE00008EBS" "5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2", // Diablo III Installer (frFR): "expand 32-byte kRY3D00007YA2YE6X0000XS4PQA8V0000ZDE45KVB0000LGC5" "478JD2K56EVNVVY4XX8TDWYT5B8KB254", // Diablo III Installer (itIT): "expand 32-byte kVVY40000B2546EVN00005B8KD2K50000DWYT478J0000XX8T" "8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A", // Diablo III Installer (koKR): "expand 32-byte k6YWH0000474ARZTN0000NVWDVNFQ0000VDH98TS40000E9CH" "LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB", // Diablo III Installer (plPL): "expand 32-byte k4ZJJ0000BLJBF4LZ0000A6GAZ32D00003AZQLJ520000XVKK" "K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG", // Diablo III Installer (ptBR): "expand 32-byte k545Y0000XYAGCUE20000WHE7HY2E0000JPVYK6BD0000KNLB" "NDVW8GWLAYCRPGRNY8RT7ZZUQU63VLPR", // Diablo III Installer (ruRU): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H", // Diablo III Installer (zhTW): "expand 32-byte kMRUC0000AA8HV3ZZ0000UX2TQTN80000A8CG6VWC0000ZXV8" // "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", // Diablo III Installer (zhCN): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // Starcraft II (Wings of Liberty): Installer.exe (4.1.1.4219) // Address of decryption routine: 0053A3D0 // Pointer to decryptor object: ECX // Pointer to key: ECX+0x5C // Authentication code URL: http://dist.blizzard.com/mediakey/sc2-authenticationcode-enUS.txt // -0C- -1C--08- -18--04- -14--00- -10- "Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX", // SC2 Wings of Liberty (deDE): "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V" "G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH", // SC2 Wings of Liberty (enGB): "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D" "W9RRHLB2FDU9WW5B3ECEBLRSFWZSF7HW", // SC2 Wings of Liberty (enSG): "expand 32-byte kWW5B0000F7HWFDU90000FWZSHLB20000BLRSW9RR00003ECE" "3DH5RE5NVM5GTFD85LXGWT6FK859ETR5", // SC2 Wings of Liberty (enUS): "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG" "8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ", // SC2 Wings of Liberty (esES): "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P" "A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54", // SC2 Wings of Liberty (esMX): "expand 32-byte kSQBR00004G54HGGX0000MF9GXX3V0000FFDXA34D0000FE5U" "ZG7J9K938HJEFWPQUA768MA2PFER6EAJ", // SC2 Wings of Liberty (frFR): "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76" "NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2", // SC2 Wings of Liberty (itIT): "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B" "3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F", // SC2 Wings of Liberty (koKR): "expand 32-byte kQWK70000838FBM9Q0000WQDB2FTM0000MWAZ3V9E0000U6MA" "2NSFB8MELULJ83U6YHA3UP6K4MQD48L6", // SC2 Wings of Liberty (plPL): "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3" "QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E", // SC2 Wings of Liberty (ptBR): "expand 32-byte kU8BM0000SW4EZ4CU00005F9CZ9EW0000CTY6QA2T0000B5WX" "VHB378W64BAT9SH7D68VV9NLQDK9YEGT", // SC2 Wings of Liberty (ruRU): "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V" "U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE", // SC2 Wings of Liberty (zhTW): "expand 32-byte k7KBN0000D9NEM6GC0000N3PLQJV400003BRDU3NF00009XQJ" NULL }; static void CreateKeyFromAuthCode( LPBYTE pbKeyBuffer, const char * szAuthCode) { PDWORD KeyPosition = (PDWORD)(pbKeyBuffer + 0x10); PDWORD AuthCode32 = (PDWORD)szAuthCode; memcpy(pbKeyBuffer, szKeyTemplate, ENCRYPTED_CHUNK_SIZE); KeyPosition[0x00] = AuthCode32[0x03]; KeyPosition[0x02] = AuthCode32[0x07]; KeyPosition[0x03] = AuthCode32[0x02]; KeyPosition[0x05] = AuthCode32[0x06]; KeyPosition[0x06] = AuthCode32[0x01]; KeyPosition[0x08] = AuthCode32[0x05]; KeyPosition[0x09] = AuthCode32[0x00]; KeyPosition[0x0B] = AuthCode32[0x04]; BSWAP_ARRAY32_UNSIGNED(pbKeyBuffer, ENCRYPTED_CHUNK_SIZE); } static void DecryptFileChunk( DWORD * ChunkData, LPBYTE pbKey, ULONGLONG ByteOffset, DWORD dwLength) { ULONGLONG ChunkOffset; DWORD KeyShuffled[0x10]; DWORD KeyMirror[0x10]; DWORD RoundCount = 0x14; // Prepare the key ChunkOffset = ByteOffset / ENCRYPTED_CHUNK_SIZE; memcpy(KeyMirror, pbKey, ENCRYPTED_CHUNK_SIZE); BSWAP_ARRAY32_UNSIGNED(KeyMirror, ENCRYPTED_CHUNK_SIZE); KeyMirror[0x05] = (DWORD)(ChunkOffset >> 32); KeyMirror[0x08] = (DWORD)(ChunkOffset); while(dwLength >= ENCRYPTED_CHUNK_SIZE) { // Shuffle the key - part 1 KeyShuffled[0x0E] = KeyMirror[0x00]; KeyShuffled[0x0C] = KeyMirror[0x01]; KeyShuffled[0x05] = KeyMirror[0x02]; KeyShuffled[0x0F] = KeyMirror[0x03]; KeyShuffled[0x0A] = KeyMirror[0x04]; KeyShuffled[0x07] = KeyMirror[0x05]; KeyShuffled[0x0B] = KeyMirror[0x06]; KeyShuffled[0x09] = KeyMirror[0x07]; KeyShuffled[0x03] = KeyMirror[0x08]; KeyShuffled[0x06] = KeyMirror[0x09]; KeyShuffled[0x08] = KeyMirror[0x0A]; KeyShuffled[0x0D] = KeyMirror[0x0B]; KeyShuffled[0x02] = KeyMirror[0x0C]; KeyShuffled[0x04] = KeyMirror[0x0D]; KeyShuffled[0x01] = KeyMirror[0x0E]; KeyShuffled[0x00] = KeyMirror[0x0F]; // Shuffle the key - part 2 for(DWORD i = 0; i < RoundCount; i += 2) { KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x02]), 0x07); KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0E]), 0x09); KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x0A]), 0x0D); KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x03]), 0x12); KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x04]), 0x07); KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x0C]), 0x09); KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x07]), 0x0D); KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x06]), 0x12); KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x01]), 0x07); KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x05]), 0x09); KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x0B]), 0x0D); KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x08]), 0x12); KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x00]), 0x07); KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x0F]), 0x09); KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x09]), 0x0D); KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x0D]), 0x12); KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x09]), 0x07); KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x0E]), 0x09); KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x04]), 0x0D); KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x08]), 0x12); KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x0A]), 0x07); KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x0C]), 0x09); KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x01]), 0x0D); KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0D]), 0x12); KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x07]), 0x07); KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x05]), 0x09); KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x00]), 0x0D); KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x03]), 0x12); KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x0B]), 0x07); KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x0F]), 0x09); KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x02]), 0x0D); KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x06]), 0x12); } // Decrypt one data chunk BSWAP_ARRAY32_UNSIGNED(ChunkData, ENCRYPTED_CHUNK_SIZE); ChunkData[0x00] = ChunkData[0x00] ^ (KeyShuffled[0x0E] + KeyMirror[0x00]); ChunkData[0x01] = ChunkData[0x01] ^ (KeyShuffled[0x04] + KeyMirror[0x0D]); ChunkData[0x02] = ChunkData[0x02] ^ (KeyShuffled[0x08] + KeyMirror[0x0A]); ChunkData[0x03] = ChunkData[0x03] ^ (KeyShuffled[0x09] + KeyMirror[0x07]); ChunkData[0x04] = ChunkData[0x04] ^ (KeyShuffled[0x0A] + KeyMirror[0x04]); ChunkData[0x05] = ChunkData[0x05] ^ (KeyShuffled[0x0C] + KeyMirror[0x01]); ChunkData[0x06] = ChunkData[0x06] ^ (KeyShuffled[0x01] + KeyMirror[0x0E]); ChunkData[0x07] = ChunkData[0x07] ^ (KeyShuffled[0x0D] + KeyMirror[0x0B]); ChunkData[0x08] = ChunkData[0x08] ^ (KeyShuffled[0x03] + KeyMirror[0x08]); ChunkData[0x09] = ChunkData[0x09] ^ (KeyShuffled[0x07] + KeyMirror[0x05]); ChunkData[0x0A] = ChunkData[0x0A] ^ (KeyShuffled[0x05] + KeyMirror[0x02]); ChunkData[0x0B] = ChunkData[0x0B] ^ (KeyShuffled[0x00] + KeyMirror[0x0F]); ChunkData[0x0C] = ChunkData[0x0C] ^ (KeyShuffled[0x02] + KeyMirror[0x0C]); ChunkData[0x0D] = ChunkData[0x0D] ^ (KeyShuffled[0x06] + KeyMirror[0x09]); ChunkData[0x0E] = ChunkData[0x0E] ^ (KeyShuffled[0x0B] + KeyMirror[0x06]); ChunkData[0x0F] = ChunkData[0x0F] ^ (KeyShuffled[0x0F] + KeyMirror[0x03]); BSWAP_ARRAY32_UNSIGNED(ChunkData, ENCRYPTED_CHUNK_SIZE); // Update byte offset in the key KeyMirror[0x08]++; if(KeyMirror[0x08] == 0) KeyMirror[0x05]++; // Move pointers and decrease number of bytes to decrypt ChunkData += (ENCRYPTED_CHUNK_SIZE / sizeof(DWORD)); dwLength -= ENCRYPTED_CHUNK_SIZE; } } static bool EncrStream_DetectFileKey(TEncryptedStream * pStream) { ULONGLONG ByteOffset = 0; BYTE EncryptedHeader[ENCRYPTED_CHUNK_SIZE]; BYTE FileHeader[ENCRYPTED_CHUNK_SIZE]; // Read the first file chunk if(pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader))) { // We just try all known keys one by one for(int i = 0; AuthCodeArray[i] != NULL; i++) { // Prepare they decryption key from game serial number CreateKeyFromAuthCode(pStream->Key, AuthCodeArray[i]); // Try to decrypt with the given key memcpy(FileHeader, EncryptedHeader, ENCRYPTED_CHUNK_SIZE); DecryptFileChunk((PDWORD)FileHeader, pStream->Key, ByteOffset, ENCRYPTED_CHUNK_SIZE); // We check the decrypted data // All known encrypted archives have header at the begin of the file, // so we check for archive signature there. if(FileHeader[0] == 'M' && FileHeader[1] == 'P' && FileHeader[2] == 'Q') { // Update the stream size pStream->StreamSize = pStream->Base.File.FileSize; // Fill the block information pStream->BlockSize = ENCRYPTED_CHUNK_SIZE; pStream->BlockCount = (DWORD)(pStream->Base.File.FileSize + ENCRYPTED_CHUNK_SIZE - 1) / ENCRYPTED_CHUNK_SIZE; pStream->IsComplete = 1; return true; } } } // Key not found, sorry return false; } static bool EncrStream_BlockRead( TEncryptedStream * pStream, ULONGLONG StartOffset, ULONGLONG EndOffset, LPBYTE BlockBuffer, DWORD BytesNeeded, bool bAvailable) { DWORD dwBytesToRead; assert((StartOffset & (pStream->BlockSize - 1)) == 0); assert(StartOffset < EndOffset); assert(bAvailable != false); BytesNeeded = BytesNeeded; bAvailable = bAvailable; // Read the file from the stream as-is // Limit the reading to number of blocks really needed dwBytesToRead = (DWORD)(EndOffset - StartOffset); if(!pStream->BaseRead(pStream, &StartOffset, BlockBuffer, dwBytesToRead)) return false; // Decrypt the data dwBytesToRead = (dwBytesToRead + ENCRYPTED_CHUNK_SIZE - 1) & ~(ENCRYPTED_CHUNK_SIZE - 1); DecryptFileChunk((PDWORD)BlockBuffer, pStream->Key, StartOffset, dwBytesToRead); return true; } static TFileStream * EncrStream_Open(LPCTSTR szFileName, DWORD dwStreamFlags) { TEncryptedStream * pStream; // Create new empty stream pStream = (TEncryptedStream *)AllocateFileStream(szFileName, sizeof(TEncryptedStream), dwStreamFlags); if(pStream == NULL) return NULL; // Attempt to open the base stream assert(pStream->BaseOpen != NULL); if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) return NULL; // Determine the encryption key for the archive if(EncrStream_DetectFileKey(pStream)) { // Set the stream position and size assert(pStream->StreamSize != 0); pStream->StreamPos = 0; pStream->dwFlags |= STREAM_FLAG_READ_ONLY; // Set new function pointers pStream->StreamRead = (STREAM_READ)BlockStream_Read; pStream->StreamGetPos = BlockStream_GetPos; pStream->StreamGetSize = BlockStream_GetSize; pStream->StreamClose = pStream->BaseClose; // Supply the block functions pStream->BlockRead = (BLOCK_READ)EncrStream_BlockRead; return pStream; } // Cleanup the stream and return FileStream_Close(pStream); SetCascError(ERROR_FILE_ENCRYPTED); return NULL; } //----------------------------------------------------------------------------- // Local functions - Block4 stream support #define BLOCK4_BLOCK_SIZE 0x4000 // Size of one block #define BLOCK4_HASH_SIZE 0x20 // Size of MD5 hash that is after each block #define BLOCK4_MAX_BLOCKS 0x00002000 // Maximum amount of blocks per file #define BLOCK4_MAX_FSIZE 0x08040000 // Max size of one file static bool Block4Stream_BlockRead( TBlockStream * pStream, // Pointer to an open stream ULONGLONG StartOffset, ULONGLONG EndOffset, LPBYTE BlockBuffer, DWORD BytesNeeded, bool bAvailable) { TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap; ULONGLONG ByteOffset; DWORD BytesToRead; DWORD StreamIndex; DWORD BlockIndex; bool bResult; // The starting offset must be aligned to size of the block assert(pStream->FileBitmap != NULL); assert((StartOffset & (pStream->BlockSize - 1)) == 0); assert(StartOffset < EndOffset); assert(bAvailable == true); // Keep compiler happy bAvailable = bAvailable; EndOffset = EndOffset; while(BytesNeeded != 0) { // Calculate the block index and the file index StreamIndex = (DWORD)((StartOffset / pStream->BlockSize) / BLOCK4_MAX_BLOCKS); BlockIndex = (DWORD)((StartOffset / pStream->BlockSize) % BLOCK4_MAX_BLOCKS); if(StreamIndex > pStream->BitmapSize) return false; // Calculate the block offset ByteOffset = ((ULONGLONG)BlockIndex * (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE)); BytesToRead = CASCLIB_MIN(BytesNeeded, BLOCK4_BLOCK_SIZE); // Read from the base stream pStream->Base = BaseArray[StreamIndex]; bResult = pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead); BaseArray[StreamIndex] = pStream->Base; // Did the result succeed? if(bResult == false) return false; // Move pointers StartOffset += BytesToRead; BlockBuffer += BytesToRead; BytesNeeded -= BytesToRead; } return true; } static void Block4Stream_Close(TBlockStream * pStream) { TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap; // If we have a non-zero count of base streams, // we have to close them all if(BaseArray != NULL) { // Close all base streams for(DWORD i = 0; i < pStream->BitmapSize; i++) { memcpy(&pStream->Base, BaseArray + i, sizeof(TBaseProviderData)); pStream->BaseClose(pStream); } } // Free the data map, if any CASC_FREE(pStream->FileBitmap); // Do not call the BaseClose function, // we closed all handles already return; } static TFileStream * Block4Stream_Open(LPCTSTR szFileName, DWORD dwStreamFlags) { TBaseProviderData * NewBaseArray = NULL; ULONGLONG RemainderBlock; ULONGLONG BlockCount; ULONGLONG FileSize; TBlockStream * pStream; LPTSTR szNameBuff; size_t nNameLength; DWORD dwBaseFiles = 0; DWORD dwBaseFlags; // Create new empty stream pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); if(pStream == NULL) return NULL; // Sanity check assert(pStream->BaseOpen != NULL); // Get the length of the file name without numeric suffix nNameLength = _tcslen(pStream->szFileName); if(pStream->szFileName[nNameLength - 2] == '.' && pStream->szFileName[nNameLength - 1] == '0') nNameLength -= 2; pStream->szFileName[nNameLength] = 0; // Supply the stream functions pStream->StreamRead = (STREAM_READ)BlockStream_Read; pStream->StreamGetSize = BlockStream_GetSize; pStream->StreamGetPos = BlockStream_GetPos; pStream->StreamClose = (STREAM_CLOSE)Block4Stream_Close; pStream->BlockRead = (BLOCK_READ)Block4Stream_BlockRead; // Allocate work space for numeric names szNameBuff = CASC_ALLOC(nNameLength + 4); if(szNameBuff != NULL) { // Set the base flags dwBaseFlags = (dwStreamFlags & STREAM_PROVIDERS_MASK) | STREAM_FLAG_READ_ONLY; // Go all suffixes from 0 to 30 for(int nSuffix = 0; nSuffix < 30; nSuffix++) { // Open the n-th file CascStrPrintf(szNameBuff, (nNameLength + 4), _T("%s.%u"), pStream->szFileName, nSuffix); if(!pStream->BaseOpen(pStream, szNameBuff, dwBaseFlags)) break; // If the open succeeded, we re-allocate the base provider array NewBaseArray = CASC_ALLOC(dwBaseFiles + 1); if(NewBaseArray == NULL) { SetCascError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } // Copy the old base data array to the new base data array if(pStream->FileBitmap != NULL) { memcpy(NewBaseArray, pStream->FileBitmap, sizeof(TBaseProviderData) * dwBaseFiles); CASC_FREE(pStream->FileBitmap); } // Also copy the opened base array memcpy(NewBaseArray + dwBaseFiles, &pStream->Base, sizeof(TBaseProviderData)); pStream->FileBitmap = NewBaseArray; dwBaseFiles++; // Get the size of the base stream pStream->BaseGetSize(pStream, &FileSize); assert(FileSize <= BLOCK4_MAX_FSIZE); RemainderBlock = FileSize % (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); BlockCount = FileSize / (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); // Increment the stream size and number of blocks pStream->StreamSize += (BlockCount * BLOCK4_BLOCK_SIZE); pStream->BlockCount += (DWORD)BlockCount; // Is this the last file? if(FileSize < BLOCK4_MAX_FSIZE) { if(RemainderBlock) { pStream->StreamSize += (RemainderBlock - BLOCK4_HASH_SIZE); pStream->BlockCount++; } break; } } // Fill the remainining block stream variables pStream->BitmapSize = dwBaseFiles; pStream->BlockSize = BLOCK4_BLOCK_SIZE; pStream->IsComplete = 1; pStream->IsModified = 0; // Fill the remaining stream variables pStream->StreamPos = 0; pStream->dwFlags |= STREAM_FLAG_READ_ONLY; CASC_FREE(szNameBuff); } // If we opened something, return success if(dwBaseFiles == 0) { FileStream_Close(pStream); SetCascError(ERROR_FILE_NOT_FOUND); pStream = NULL; } return pStream; } //----------------------------------------------------------------------------- // Public functions /** * This function creates a new file for read-write access * * - If the current platform supports file sharing, * the file must be created for read sharing (i.e. another application * can open the file for read, but not for write) * - If the file does not exist, the function must create new one * - If the file exists, the function must rewrite it and set to zero size * - The parameters of the function must be validate by the caller * - The function must initialize all stream function pointers in TFileStream * - If the function fails from any reason, it must close all handles * and free all memory that has been allocated in the process of stream creation, * including the TFileStream structure itself * * \a szFileName Name of the file to create */ TFileStream * FileStream_CreateFile( LPCTSTR szFileName, DWORD dwStreamFlags) { TFileStream * pStream; // We only support creation of flat, local file if((dwStreamFlags & (STREAM_PROVIDERS_MASK)) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE)) { SetCascError(ERROR_NOT_SUPPORTED); return NULL; } // Allocate file stream structure for flat stream pStream = AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); if(pStream != NULL) { // Attempt to create the disk file if(BaseFile_Create(pStream)) { // Fill the stream provider functions pStream->StreamRead = pStream->BaseRead; pStream->StreamWrite = pStream->BaseWrite; pStream->StreamResize = pStream->BaseResize; pStream->StreamGetSize = pStream->BaseGetSize; pStream->StreamGetPos = pStream->BaseGetPos; pStream->StreamClose = pStream->BaseClose; return pStream; } // File create failed, delete the stream CASC_FREE(pStream); } // Return the stream return pStream; } /** * This function opens an existing file for read or read-write access * - If the current platform supports file sharing, * the file must be open for read sharing (i.e. another application * can open the file for read, but not for write) * - If the file does not exist, the function must return NULL * - If the file exists but cannot be open, then function must return NULL * - The parameters of the function must be validate by the caller * - The function must initialize all stream function pointers in TFileStream * - If the function fails from any reason, it must close all handles * and free all memory that has been allocated in the process of stream creation, * including the TFileStream structure itself * * \a szFileName Name of the file to open * \a dwStreamFlags specifies the provider and base storage type */ TFileStream * FileStream_OpenFile( LPCTSTR szFileName, DWORD dwStreamFlags) { DWORD dwProvider = dwStreamFlags & STREAM_PROVIDERS_MASK; size_t nPrefixLength = FileStream_Prefix(szFileName, &dwProvider); // Re-assemble the stream flags dwStreamFlags = (dwStreamFlags & STREAM_OPTIONS_MASK) | dwProvider; szFileName += nPrefixLength; // Perform provider-specific open switch(dwStreamFlags & STREAM_PROVIDER_MASK) { case STREAM_PROVIDER_FLAT: return FlatStream_Open(szFileName, dwStreamFlags); case STREAM_PROVIDER_PARTIAL: return PartStream_Open(szFileName, dwStreamFlags); case STREAM_PROVIDER_ENCRYPTED: return EncrStream_Open(szFileName, dwStreamFlags); case STREAM_PROVIDER_BLOCK4: return Block4Stream_Open(szFileName, dwStreamFlags); default: SetCascError(ERROR_INVALID_PARAMETER); return NULL; } } /** * Returns the file name of the stream * * \a pStream Pointer to an open stream */ LPCTSTR FileStream_GetFileName(TFileStream * pStream) { assert(pStream != NULL); return pStream->szFileName; } /** * Returns the length of the provider prefix. Returns zero if no prefix * * \a szFileName Pointer to a stream name (file, mapped file, URL) * \a pdwStreamProvider Pointer to a DWORD variable that receives stream provider (STREAM_PROVIDER_XXX) */ size_t FileStream_Prefix(LPCTSTR szFileName, DWORD * pdwProvider) { size_t nPrefixLength1 = 0; size_t nPrefixLength2 = 0; DWORD dwProvider = 0; if(szFileName != NULL) { // // Determine the stream provider // if(!_tcsnicmp(szFileName, _T("flat-"), 5)) { dwProvider |= STREAM_PROVIDER_FLAT; nPrefixLength1 = 5; } else if(!_tcsnicmp(szFileName, _T("part-"), 5)) { dwProvider |= STREAM_PROVIDER_PARTIAL; nPrefixLength1 = 5; } else if(!_tcsnicmp(szFileName, _T("mpqe-"), 5)) { dwProvider |= STREAM_PROVIDER_ENCRYPTED; nPrefixLength1 = 5; } else if(!_tcsnicmp(szFileName, _T("blk4-"), 5)) { dwProvider |= STREAM_PROVIDER_BLOCK4; nPrefixLength1 = 5; } // Cut out the stream provider szFileName += nPrefixLength1; // // Determine the base provider // if(!_tcsnicmp(szFileName, _T("file:"), 5)) { dwProvider |= BASE_PROVIDER_FILE; nPrefixLength2 = 5; } else if(!_tcsnicmp(szFileName, _T("map:"), 4)) { dwProvider |= BASE_PROVIDER_MAP; nPrefixLength2 = 4; } else if(!_tcsnicmp(szFileName, _T("http:"), 5)) { dwProvider |= BASE_PROVIDER_HTTP; nPrefixLength2 = 5; } else if(!_tcsnicmp(szFileName, _T("ribbit:"), 7)) { dwProvider |= BASE_PROVIDER_RIBBIT; nPrefixLength2 = 7; } // Only accept stream provider if we recognized the base provider if(nPrefixLength2 != 0) { // It is also allowed to put "//" after the base provider, e.g. "file://", "http://" if(szFileName[nPrefixLength2] == '/' && szFileName[nPrefixLength2+1] == '/') nPrefixLength2 += 2; if(pdwProvider != NULL) *pdwProvider = dwProvider; } } return nPrefixLength1 + nPrefixLength2; } /** * Sets a download callback. Whenever the stream needs to download one or more blocks * from the server, the callback is called * * \a pStream Pointer to an open stream * \a pfnCallback Pointer to callback function * \a pvUserData Arbitrary user pointer passed to the download callback */ bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData) { TBlockStream * pBlockStream = (TBlockStream *)pStream; if(pStream->BlockRead == NULL) { SetCascError(ERROR_NOT_SUPPORTED); return false; } pBlockStream->pfnCallback = pfnCallback; pBlockStream->UserData = pvUserData; return true; } /** * Reads data from the stream * * - Returns true if the read operation succeeded and all bytes have been read * - Returns false if either read failed or not all bytes have been read * - If the pByteOffset is NULL, the function must read the data from the current file position * - The function can be called with dwBytesToRead = 0. In that case, pvBuffer is ignored * and the function just adjusts file pointer. * * \a pStream Pointer to an open stream * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position * \a pvBuffer Pointer to data to be read * \a dwBytesToRead Number of bytes to read from the file * * \returns * - If the function reads the required amount of bytes, it returns true. * - If the function reads less than required bytes, it returns false and GetCascError() returns ERROR_HANDLE_EOF * - If the function fails, it reads false and GetCascError() returns an error code different from ERROR_HANDLE_EOF */ bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead) { assert(pStream->StreamRead != NULL); return pStream->StreamRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); } /** * This function writes data to the stream * * - Returns true if the write operation succeeded and all bytes have been written * - Returns false if either write failed or not all bytes have been written * - If the pByteOffset is NULL, the function must write the data to the current file position * * \a pStream Pointer to an open stream * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position * \a pvBuffer Pointer to data to be written * \a dwBytesToWrite Number of bytes to write to the file */ bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) { if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) { SetCascError(ERROR_ACCESS_DENIED); return false; } assert(pStream->StreamWrite != NULL); return pStream->StreamWrite(pStream, pByteOffset, pvBuffer, dwBytesToWrite); } /** * Returns the size of a file * * \a pStream Pointer to an open stream * \a FileSize Pointer where to store the file size */ bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) { assert(pStream->StreamGetSize != NULL); return pStream->StreamGetSize(pStream, pFileSize); } /** * Sets the size of a file * * \a pStream Pointer to an open stream * \a NewFileSize File size to set */ bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) { if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) { SetCascError(ERROR_ACCESS_DENIED); return false; } assert(pStream->StreamResize != NULL); return pStream->StreamResize(pStream, NewFileSize); } /** * This function returns the current file position * \a pStream * \a pByteOffset */ bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) { assert(pStream->StreamGetPos != NULL); return pStream->StreamGetPos(pStream, pByteOffset); } /** * Returns the last write time of a file * * \a pStream Pointer to an open stream * \a pFileType Pointer where to store the file last write time */ bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) { // Just use the saved filetime value *pFileTime = pStream->Base.File.FileTime; return true; } /** * Returns the stream flags * * \a pStream Pointer to an open stream * \a pdwStreamFlags Pointer where to store the stream flags */ bool FileStream_GetFlags(TFileStream * pStream, PDWORD pdwStreamFlags) { *pdwStreamFlags = pStream->dwFlags; return true; } /** * Switches a stream with another. Used for final phase of archive compacting. * Performs these steps: * * 1) Closes the handle to the existing file * 2) Renames the temporary file to the original file, overwrites existing one * 3) Opens the file stores the handle and stream position to the new stream structure * * \a pStream Pointer to an open stream * \a pNewStream Temporary ("working") stream (created during archive compacting) */ bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream) { // Only supported on flat files if((pStream->dwFlags & STREAM_PROVIDERS_MASK) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE)) { SetCascError(ERROR_NOT_SUPPORTED); return false; } // Not supported on read-only streams if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) { SetCascError(ERROR_ACCESS_DENIED); return false; } // Close both stream's base providers pNewStream->BaseClose(pNewStream); pStream->BaseClose(pStream); // Now we have to delete the (now closed) old file and rename the new file if(!BaseFile_Replace(pStream, pNewStream)) return false; // Now open the base file again if(!BaseFile_Open(pStream, pStream->szFileName, pStream->dwFlags)) return false; // Cleanup the new stream FileStream_Close(pNewStream); return true; } /** * This function closes an archive file and frees any data buffers * that have been allocated for stream management. The function must also * support partially allocated structure, i.e. one or more buffers * can be NULL, if there was an allocation failure during the process * * \a pStream Pointer to an open stream */ void FileStream_Close(TFileStream * pStream) { // Check if the stream structure is allocated at all if(pStream != NULL) { // Free the master stream, if any if(pStream->pMaster != NULL) FileStream_Close(pStream->pMaster); pStream->pMaster = NULL; // Close the stream provider. if(pStream->StreamClose != NULL) pStream->StreamClose(pStream); // Also close base stream, if any else if(pStream->BaseClose != NULL) pStream->BaseClose(pStream); // Free the stream lock CascFreeLock(pStream->Lock); // Free the stream itself CASC_FREE(pStream); } } ================================================ FILE: deps/CascLib/src/common/FileStream.h ================================================ /*****************************************************************************/ /* FileStream.h Copyright (c) Ladislav Zezula 2012 */ /*---------------------------------------------------------------------------*/ /* Description: Definitions for FileStream object */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 14.04.12 1.00 Lad The first version of FileStream.h */ /*****************************************************************************/ #ifndef __FILESTREAM_H__ #define __FILESTREAM_H__ //----------------------------------------------------------------------------- // Flags for file stream #define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file #define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file #define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server via the HTTP protocol #define BASE_PROVIDER_RIBBIT 0x00000003 // Base data source is a file on web server via the Ribbit protocol #define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value #define STREAM_PROVIDER_FLAT 0x00000000 // Stream is linear with no offset mapping #define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part) #define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted archive #define STREAM_PROVIDER_BLOCK4 0x00000030 // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file #define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value #define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only #define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write #define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it #define STREAM_FLAG_FILL_MISSING 0x00000800 // If less than expected was read from the file, fill the missing part with zeros #define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options #define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers #define STREAM_FLAGS_MASK 0x0000FFFF // Mask for all stream flags (providers+options) //----------------------------------------------------------------------------- // Function prototypes typedef void (*STREAM_INIT)( struct TFileStream * pStream // Pointer to an unopened stream ); typedef bool (*STREAM_CREATE)( struct TFileStream * pStream // Pointer to an unopened stream ); typedef bool (*STREAM_OPEN)( struct TFileStream * pStream, // Pointer to an unopened stream LPCTSTR szFileName, // Pointer to file name to be open DWORD dwStreamFlags // Stream flags ); typedef bool (*STREAM_READ)( struct TFileStream * pStream, // Pointer to an open stream ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position void * pvBuffer, // Pointer to data to be read DWORD dwBytesToRead // Number of bytes to read from the file ); typedef bool (*STREAM_WRITE)( struct TFileStream * pStream, // Pointer to an open stream ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position const void * pvBuffer, // Pointer to data to be written DWORD dwBytesToWrite // Number of bytes to read from the file ); typedef bool (*STREAM_RESIZE)( struct TFileStream * pStream, // Pointer to an open stream ULONGLONG FileSize // New size for the file, in bytes ); typedef bool (*STREAM_GETSIZE)( struct TFileStream * pStream, // Pointer to an open stream ULONGLONG * pFileSize // Receives the file size, in bytes ); typedef bool (*STREAM_GETPOS)( struct TFileStream * pStream, // Pointer to an open stream ULONGLONG * pByteOffset // Pointer to store current file position ); typedef void (*STREAM_CLOSE)( struct TFileStream * pStream // Pointer to an open stream ); typedef bool (*BLOCK_READ)( struct TFileStream * pStream, // Pointer to a block-oriented stream ULONGLONG StartOffset, // Byte offset of start of the block array ULONGLONG EndOffset, // End offset (either end of the block or end of the file) LPBYTE BlockBuffer, // Pointer to block-aligned buffer DWORD BytesNeeded, // Number of bytes that are really needed bool bAvailable // true if the block is available ); typedef bool (*BLOCK_CHECK)( struct TFileStream * pStream, // Pointer to a block-oriented stream ULONGLONG BlockOffset // Offset of the file to check ); typedef void (*BLOCK_SAVEMAP)( struct TFileStream * pStream // Pointer to a block-oriented stream ); typedef void (WINAPI * STREAM_DOWNLOAD_CALLBACK)( void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes ); //----------------------------------------------------------------------------- // Local structures - partial file structure and bitmap footer #define ID_FILE_BITMAP_FOOTER 0x33767470 // Signature of the file bitmap footer ('ptv3') #define DEFAULT_BLOCK_SIZE 0x00004000 // Default size of the stream block #define DEFAULT_BUILD_NUMBER 10958 // Build number for newly created partial MPQs typedef struct _PART_FILE_HEADER { DWORD PartialVersion; // Always set to 2 char GameBuildNumber[0x20]; // Minimum build number of the game that can use this MPQ DWORD Flags; // Flags (details unknown) DWORD FileSizeLo; // Low 32 bits of the contained file size DWORD FileSizeHi; // High 32 bits of the contained file size DWORD BlockSize; // Size of one file block, in bytes } PART_FILE_HEADER, *PPART_FILE_HEADER; // Structure describing the block-to-file map entry typedef struct _PART_FILE_MAP_ENTRY { DWORD Flags; // 3 = the block is present in the file DWORD BlockOffsLo; // Low 32 bits of the block position in the file DWORD BlockOffsHi; // High 32 bits of the block position in the file DWORD LargeValueLo; // 64-bit value, meaning is unknown DWORD LargeValueHi; } PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY; typedef struct _FILE_BITMAP_FOOTER { DWORD Signature; // 'ptv3' (ID_FILE_BITMAP_FOOTER) DWORD Version; // Unknown, seems to always have value of 3 (version?) DWORD BuildNumber; // Game build number for that MPQ DWORD MapOffsetLo; // Low 32-bits of the offset of the bit map DWORD MapOffsetHi; // High 32-bits of the offset of the bit map DWORD BlockSize; // Size of one block (usually 0x4000 bytes) } FILE_BITMAP_FOOTER, *PFILE_BITMAP_FOOTER; //----------------------------------------------------------------------------- // Structure for file stream union TBaseProviderData { struct { ULONGLONG FileSize; // Size of the file ULONGLONG FilePos; // Current file position ULONGLONG FileTime; // Last write time HANDLE hFile; // File handle } File; struct { ULONGLONG FileSize; // Size of the file ULONGLONG FilePos; // Current file position ULONGLONG FileTime; // Last write time LPBYTE pbFile; // Pointer to mapped view } Map; struct { class CASC_SOCKET * pSocket; // An open socket unsigned char * fileData; // Raw response converted to file data char * hostName; // Name of the remote host char * fileName; // Name of the remote resource size_t fileDataLength; // Length of the file data, in bytes size_t fileDataPos; // Current position in the data } Socket; }; struct TFileStream { // Stream provider functions STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly. STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly. STREAM_RESIZE StreamResize; // Pointer to function changing file size STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position STREAM_CLOSE StreamClose; // Pointer to function closing the stream // Block-oriented functions BLOCK_READ BlockRead; // Pointer to function reading one or more blocks BLOCK_CHECK BlockCheck; // Pointer to function checking whether the block is present // Base provider functions STREAM_CREATE BaseCreate; // Pointer to base create function STREAM_OPEN BaseOpen; // Pointer to base open function STREAM_READ BaseRead; // Read from the stream STREAM_WRITE BaseWrite; // Write to the stream STREAM_RESIZE BaseResize; // Pointer to function changing file size STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position STREAM_CLOSE BaseClose; // Pointer to function closing the stream // Base provider data (file size, file position) TBaseProviderData Base; // Stream information, like size or current position CASC_LOCK Lock; // For multi-threaded synchronization // Stream provider data TFileStream * pMaster; // Master stream (e.g. MPQ on a web server) LPTSTR szFileName; // File name (self-relative pointer) ULONGLONG StreamSize; // Stream size (can be less than file size) ULONGLONG StreamPos; // Stream position DWORD BuildNumber; // Game build number DWORD dwFlags; // Stream flags // Followed by stream provider data, with variable length }; //----------------------------------------------------------------------------- // Structures for block-oriented stream struct TBlockStream : public TFileStream { STREAM_DOWNLOAD_CALLBACK pfnCallback; // Callback for downloading void * FileBitmap; // Array of bits for file blocks void * UserData; // User data to be passed to the download callback DWORD BitmapSize; // Size of the file bitmap (in bytes) DWORD BlockSize; // Size of one block, in bytes DWORD BlockCount; // Number of data blocks in the file DWORD IsComplete; // If nonzero, no blocks are missing DWORD IsModified; // nonzero if the bitmap has been modified }; //----------------------------------------------------------------------------- // Structure for encrypted stream #define ENCRYPTED_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted struct TEncryptedStream : public TBlockStream { BYTE Key[ENCRYPTED_CHUNK_SIZE]; // File key }; //----------------------------------------------------------------------------- // Public functions for file stream TFileStream * FileStream_CreateFile(LPCTSTR szFileName, DWORD dwStreamFlags); TFileStream * FileStream_OpenFile(LPCTSTR szFileName, DWORD dwStreamFlags = 0); LPCTSTR FileStream_GetFileName(TFileStream * pStream); size_t FileStream_Prefix(LPCTSTR szFileName, DWORD * pdwProvider); bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData); bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead); bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite); bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize); bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset); bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT); bool FileStream_GetFlags(TFileStream * pStream, PDWORD pdwStreamFlags); bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream); void FileStream_Close(TFileStream * pStream); #endif // __FILESTREAM_H__ ================================================ FILE: deps/CascLib/src/common/FileTree.cpp ================================================ /*****************************************************************************/ /* FileTree.cpp Copyright (c) Ladislav Zezula 2018 */ /*---------------------------------------------------------------------------*/ /* Common implementation of a file tree object for various ROOt file formats */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.05.18 1.00 Lad The first version of FileTree.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" //----------------------------------------------------------------------------- // Local defines #define START_ITEM_COUNT 0x4000 inline DWORD GET_NODE_INT32(void * node, size_t offset) { PDWORD PtrValue = (PDWORD)((LPBYTE)node + offset); return PtrValue[0]; } inline void SET_NODE_INT32(void * node, size_t offset, DWORD value) { PDWORD PtrValue = (PDWORD)((LPBYTE)node + offset); PtrValue[0] = value; } //----------------------------------------------------------------------------- // Protected functions // Inserts a new file node to the file tree. // If the pointer to file node array changes, the function also rebuilds all maps PCASC_FILE_NODE CASC_FILE_TREE::InsertNew(PCASC_CKEY_ENTRY pCKeyEntry) { PCASC_FILE_NODE pFileNode; // Create a brand new node pFileNode = InsertNew(); if(pFileNode != NULL) { // Initialize the file node's CKeyEntry pFileNode->pCKeyEntry = pCKeyEntry; // Don't insert the node into any of the arrays here. // That is the caller's responsibility } return pFileNode; } PCASC_FILE_NODE CASC_FILE_TREE::InsertNew() { PCASC_FILE_NODE pFileNode; void * SaveItemArray = NodeTable.ItemArray(); // We need to save the array pointers. If it changes, we must rebuild both maps // Create a brand new node pFileNode = (PCASC_FILE_NODE)NodeTable.Insert(1); if(pFileNode != NULL) { // Initialize the file node pFileNode->FileNameHash = 0; pFileNode->pCKeyEntry = NULL; pFileNode->Parent = 0; pFileNode->NameIndex = 0; pFileNode->NameLength = 0; pFileNode->Flags = 0; // We need to supply a file data id for the new entry, otherwise the rebuilding function // will use the uninitialized one SetExtras(pFileNode, CASC_INVALID_ID, CASC_INVALID_ID, CASC_INVALID_ID); // If the array pointer changed or we are close to the size of the array, we need to rebuild the maps if(NodeTable.ItemArray() != SaveItemArray || (NodeTable.ItemCount() * 3 / 2) > NameMap.HashTableSize()) { // Rebuild both maps. Note that rebuilding also inserts all items to the maps, so no need to insert them here if(!RebuildNameMaps()) { pFileNode = NULL; assert(false); } } } return pFileNode; } // Insert the node to the map of FileNameHash -> CASC_FILE_NODE bool CASC_FILE_TREE::InsertToHashTable(PCASC_FILE_NODE pFileNode) { bool bResult = false; // Insert the file node to the table if(pFileNode->FileNameHash != 0) bResult = NameMap.InsertObject(pFileNode, &pFileNode->FileNameHash); return bResult; } // Inserts the file node to the array of file data ids bool CASC_FILE_TREE::InsertToIdTable(PCASC_FILE_NODE pFileNode) { PCASC_FILE_NODE * RefElement; DWORD FileDataId = CASC_INVALID_ID; if(FileDataIds.IsInitialized()) { // Retrieve the file data id GetExtras(pFileNode, &FileDataId, NULL, NULL); if(FileDataId != CASC_INVALID_ID) { // Sanity check assert(FileDataId < 0x10000000); // Insert the element to the array RefElement = (PCASC_FILE_NODE *)FileDataIds.InsertAt(FileDataId); if(RefElement != NULL) { RefElement[0] = pFileNode; return true; } } } return false; } bool CASC_FILE_TREE::SetNodePlainName(PCASC_FILE_NODE pFileNode, const char * szPlainName, const char * szPlainNameEnd) { char * szNodeName; size_t nLength = (szPlainNameEnd - szPlainName); // Insert all chars to the name array szNodeName = (char *)NameTable.Insert(nLength); if(szNodeName != NULL) { // Copy the plain name to the node. Do not include the string terminator memcpy(szNodeName, szPlainName, nLength); // Supply the file name to the file node pFileNode->NameIndex = (DWORD)NameTable.IndexOf(szNodeName); pFileNode->NameLength = (USHORT)nLength; return true; } return false; } bool CASC_FILE_TREE::SetKeyLength(DWORD aKeyLength) { if(aKeyLength > MD5_HASH_SIZE) return false; KeyLength = aKeyLength; return true; } DWORD CASC_FILE_TREE::GetNextFileDataId() { if(FileDataIds.IsInitialized()) return (DWORD)(FileDataIds.ItemCount() + 1); return CASC_INVALID_ID; } bool CASC_FILE_TREE::RebuildNameMaps() { PCASC_FILE_NODE pFileNode; size_t nMaxItems = NodeTable.ItemCountMax(); // Free the map of "FullName -> CASC_FILE_NODE" NameMap.Free(); // Create new map map "FullName -> CASC_FILE_NODE" if(NameMap.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_NODE, FileNameHash)) != ERROR_SUCCESS) return false; // Reset the entire array, but keep the buffer allocated FileDataIds.Reset(); // Parse all items and insert them to the map for(size_t i = 0; i < NodeTable.ItemCount(); i++) { // Retrieve the n-th object pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(i); if(pFileNode != NULL) { // Insert it to the map "FileNameHash -> CASC_FILE_NODE" if(pFileNode->FileNameHash != 0) InsertToHashTable(pFileNode); // Insert it to the array "FileDataId -> CASC_FILE_NODE" if(FileDataIds.IsInitialized()) InsertToIdTable(pFileNode); } } return true; } //----------------------------------------------------------------------------- // Public functions DWORD CASC_FILE_TREE::Create(DWORD Flags) { PCASC_FILE_NODE pRootNode; size_t FileNodeSize = FIELD_OFFSET(CASC_FILE_NODE, ExtraValues); DWORD dwErrCode; // Initialize the file tree memset(this, 0, sizeof(CASC_FILE_TREE)); KeyLength = MD5_HASH_SIZE; // Shall we use the data ID in the tree node? if(Flags & FTREE_FLAG_USE_DATA_ID) { // Set the offset of the file data id in the entry FileDataIdOffset = FileNodeSize; FileNodeSize += sizeof(DWORD); // Create the array for FileDataId -> CASC_FILE_NODE dwErrCode = FileDataIds.Create(START_ITEM_COUNT); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; } // Shall we use the locale ID in the tree node? if(Flags & FTREE_FLAG_USE_LOCALE_FLAGS) { LocaleFlagsOffset = FileNodeSize; FileNodeSize += sizeof(DWORD); } if(Flags & FTREE_FLAG_USE_CONTENT_FLAGS) { ContentFlagsOffset = FileNodeSize; FileNodeSize += sizeof(DWORD); } // Align the file node size to 8 bytes FileNodeSize = ALIGN_TO_SIZE(FileNodeSize, 8); // Initialize the dynamic array dwErrCode = NodeTable.Create(FileNodeSize, START_ITEM_COUNT); if(dwErrCode == ERROR_SUCCESS) { // Create the dynamic array that will hold the node names dwErrCode = NameTable.Create(START_ITEM_COUNT); if(dwErrCode == ERROR_SUCCESS) { // Insert the first "root" node, without name pRootNode = (PCASC_FILE_NODE)NodeTable.Insert(1); if(pRootNode != NULL) { // Initialize the node memset(pRootNode, 0, NodeTable.ItemSize()); pRootNode->Parent = CASC_INVALID_INDEX; pRootNode->NameIndex = CASC_INVALID_INDEX; pRootNode->Flags = CFN_FLAG_FOLDER; SetExtras(pRootNode, CASC_INVALID_ID, CASC_INVALID_ID, CASC_INVALID_ID); } } } // Create both maps if(!RebuildNameMaps()) dwErrCode = ERROR_NOT_ENOUGH_MEMORY; return dwErrCode; } void CASC_FILE_TREE::Free() { // Free both arrays NodeTable.Free(); NameTable.Free(); FileDataIds.Free(); // Free the name map NameMap.Free(); // Zero the object memset(this, 0, sizeof(CASC_FILE_TREE)); } PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags) { PCASC_FILE_NODE pFileNode; ULONGLONG FileNameHash; // Sanity checks assert(szFileName != NULL && szFileName[0] != 0); assert(pCKeyEntry != NULL); //BREAK_ON_XKEY3(pCKeyEntry->EKey, 0x00, 0x00, 0x0F); // Calculate the file name hash FileNameHash = CalcFileNameHash(szFileName); // Do nothing if the file name is there already. pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash); if(pFileNode == NULL) { // Insert new item pFileNode = InsertNew(pCKeyEntry); if(pFileNode != NULL) { // Supply the name hash pFileNode->FileNameHash = FileNameHash; // Set the file data id and the extra values SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags); // Insert the file node to the hash map InsertToHashTable(pFileNode); // Also make sure that it's in the file data id table, if the table is initialized InsertToIdTable(pFileNode); // Set the file name of the new file node SetNodeFileName(pFileNode, szFileName); // If we created a new node, we need to increment the reference count assert(pCKeyEntry->RefCount != 0xFFFF); pCKeyEntry->RefCount++; FileNodes++; } } return pFileNode; } PCASC_FILE_NODE CASC_FILE_TREE::InsertByHash(PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG FileNameHash, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags) { PCASC_FILE_NODE pFileNode; // Sanity checks assert(FileDataIds.IsInitialized()); assert(FileDataId != CASC_INVALID_ID); assert(FileNameHash != 0); assert(pCKeyEntry != NULL); // Insert the node to the tree by file data id pFileNode = InsertById(pCKeyEntry, FileDataId, LocaleFlags, ContentFlags); if(pFileNode != NULL) { // Supply the name hash pFileNode->FileNameHash = FileNameHash; // Insert the file node to the hash map InsertToHashTable(pFileNode); } return pFileNode; } PCASC_FILE_NODE CASC_FILE_TREE::InsertById(PCASC_CKEY_ENTRY pCKeyEntry, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags) { PCASC_FILE_NODE pFileNode; // Sanity checks assert(FileDataIds.IsInitialized()); assert(FileDataId != CASC_INVALID_ID); assert(pCKeyEntry != NULL); // Check whether the file data id exists in the array of file data ids if((pFileNode = FindById(FileDataId)) == NULL) { // Insert the new file node pFileNode = InsertNew(pCKeyEntry); if(pFileNode != NULL) { // Set the file data id and the extra values SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags); // Insert the file node to the FileDataId array InsertToIdTable(pFileNode); // Increment the number of references pCKeyEntry->RefCount++; } } // Return the new or old node return pFileNode; } PCASC_FILE_NODE CASC_FILE_TREE::ItemAt(size_t nItemIndex) { return (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex); } PCASC_FILE_NODE CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, size_t nItemIndex) { PCASC_FILE_NODE pFileNode = NULL; // If we have FileDataId, then we need to enumerate the files by FileDataId if(FileDataIds.IsInitialized()) pFileNode = *(PCASC_FILE_NODE *)FileDataIds.ItemAt(nItemIndex); else pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex); // Construct the entire path PathAt(szBuffer, cchBuffer, pFileNode); return pFileNode; } size_t CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, PCASC_FILE_NODE pFileNode) { PCASC_FILE_NODE pParentNode; const char * szNamePtr; char * szSaveBuffer = szBuffer; char * szBufferEnd = szBuffer + cchBuffer - 1; if(pFileNode != NULL && pFileNode->Parent != CASC_INVALID_INDEX) { // Copy all parents pParentNode = (PCASC_FILE_NODE)NodeTable.ItemAt(pFileNode->Parent); if(pParentNode != NULL) { // Query the parent and move the buffer szBuffer = szBuffer + PathAt(szBuffer, cchBuffer, pParentNode); } // Retrieve the node name szNamePtr = (const char *)NameTable.ItemAt(pFileNode->NameIndex); // Check whether we have enough space if((szBuffer + pFileNode->NameLength) < szBufferEnd) { // Copy the path part memcpy(szBuffer, szNamePtr, pFileNode->NameLength); szBuffer += pFileNode->NameLength; // Append backslash if((pFileNode->Flags & CFN_FLAG_FOLDER) && ((szBuffer + 1) < szBufferEnd)) { *szBuffer++ = (pFileNode->Flags & CFN_FLAG_MOUNT_POINT) ? ':' : '\\'; } } } // Terminate buffer with zero szBuffer[0] = 0; // Return length of the copied string return (szBuffer - szSaveBuffer); } PCASC_FILE_NODE CASC_FILE_TREE::Find(const char * szFullPath, DWORD FileDataId, PCASC_FIND_DATA pFindData) { PCASC_FILE_NODE pFileNode = NULL; ULONGLONG FileNameHash; // Can we search by FileDataId? if(FileDataIds.IsInitialized() && (FileDataId != CASC_INVALID_ID || IsFileDataIdName(szFullPath, FileDataId))) { pFileNode = FindById(FileDataId); } else { if(szFullPath != NULL && szFullPath[0] != 0) { FileNameHash = CalcFileNameHash(szFullPath); pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash); } } // Did we find anything? if(pFileNode != NULL && pFindData != NULL) { GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags); } return pFileNode; } PCASC_FILE_NODE CASC_FILE_TREE::Find(PCASC_CKEY_ENTRY pCKeyEntry) { PCASC_FILE_NODE pFileNode; for(size_t i = 0; i < NodeTable.ItemCount(); i++) { pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(i); if((pFileNode->Flags & (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT)) == 0) { if(pFileNode->pCKeyEntry == pCKeyEntry) return pFileNode; } } return NULL; } PCASC_FILE_NODE CASC_FILE_TREE::Find(ULONGLONG FileNameHash) { return (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash); } PCASC_FILE_NODE CASC_FILE_TREE::FindById(DWORD FileDataId) { PCASC_FILE_NODE * RefElement; PCASC_FILE_NODE pFileNode = NULL; if(FileDataId != CASC_INVALID_ID && FileDataIds.IsInitialized()) { // Insert the element to the array RefElement = (PCASC_FILE_NODE *)FileDataIds.ItemAt(FileDataId); if(RefElement != NULL) { pFileNode = RefElement[0]; } } return pFileNode; } bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName) { ULONGLONG FileNameHash = 0; PCASC_FILE_NODE pFolderNode = NULL; CASC_PATH PathBuffer; LPCSTR szNodeBegin = szFileName; size_t nFileNode = NodeTable.IndexOf(pFileNode); size_t i; DWORD Parent = 0; // Sanity checks assert(szFileName != NULL && szFileName[0] != 0); // Traverse the entire path. For each subfolder, we insert an appropriate fake entry for(i = 0; szFileName[i] != 0; i++) { char chOneChar = szFileName[i]; // Is there a path separator? // Note: Warcraft III paths may contain "mount points". // Example: "frFR-War3Local.mpq:Maps/FrozenThrone/Campaign/NightElfX06Interlude.w3x:war3map.j" if(chOneChar == '\\' || chOneChar == '/' || chOneChar == ':') { // Calculate hash of the file name up to the end of the node name FileNameHash = CalcNormNameHash(PathBuffer, i); // If the entry is not there yet, create new one if((pFolderNode = Find(FileNameHash)) == NULL) { // Insert new entry to the tree pFolderNode = InsertNew(); if(pFolderNode == NULL) return false; // Populate the file entry pFolderNode->FileNameHash = FileNameHash; pFolderNode->Parent = Parent; pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER; FolderNodes++; // Set the node sub name to the node SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i); // Insert the entry to the name map InsertToHashTable(pFolderNode); } // Move the parent to the current node Parent = (DWORD)NodeTable.IndexOf(pFolderNode); // Move the begin of the node after the separator szNodeBegin = szFileName + i + 1; } // Copy the next character, even if it was slash/backslash before PathBuffer.AppendChar(AsciiToUpperTable_BkSlash[chOneChar]); } // If anything left, this is gonna be our node name if(szNodeBegin < szFileName + i) { // We need to reset the file node pointer, as the file node table might have changed pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode); // Write the plain file name to the node SetNodePlainName(pFileNode, szNodeBegin, szFileName + i); pFileNode->Parent = Parent; // Also insert the node to the hash table so CascOpenFile can find it if(pFileNode->FileNameHash == 0) { pFileNode->FileNameHash = CalcNormNameHash(PathBuffer, i); InsertToHashTable(pFileNode); } } return true; } /* bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName) { ULONGLONG FileNameHash = 0; PCASC_FILE_NODE pFolderNode = NULL; LPCSTR szNodeBegin = szFileName; char szPathBuffer[MAX_PATH+1]; size_t nFileNode = NodeTable.IndexOf(pFileNode); size_t i; DWORD Parent = 0; // Sanity checks assert(szFileName != NULL && szFileName[0] != 0); // Traverse the entire path. For each subfolder, we insert an appropriate fake entry for(i = 0; szFileName[i] != 0; i++) { char chOneChar = szFileName[i]; // Is there a path separator? // Note: Warcraft III paths may contain "mount points". // Example: "frFR-War3Local.mpq:Maps/FrozenThrone/Campaign/NightElfX06Interlude.w3x:war3map.j" if(chOneChar == '\\' || chOneChar == '/' || chOneChar == ':') { // Calculate hash of the file name up to the end of the node name FileNameHash = CalcNormNameHash(szPathBuffer, i); // If the entry is not there yet, create new one if((pFolderNode = Find(FileNameHash)) == NULL) { // Insert new entry to the tree pFolderNode = InsertNew(); if(pFolderNode == NULL) return false; // Populate the file entry pFolderNode->FileNameHash = FileNameHash; pFolderNode->Parent = Parent; pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER; FolderNodes++; // Set the node sub name to the node SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i); // Insert the entry to the name map InsertToHashTable(pFolderNode); } // Move the parent to the current node Parent = (DWORD)NodeTable.IndexOf(pFolderNode); // Move the begin of the node after the separator szNodeBegin = szFileName + i + 1; } // Copy the next character, even if it was slash/backslash before szPathBuffer[i] = AsciiToUpperTable_BkSlash[chOneChar]; } // If anything left, this is gonna be our node name if(szNodeBegin < szFileName + i) { // We need to reset the file node pointer, as the file node table might have changed pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode); // Write the plain file name to the node SetNodePlainName(pFileNode, szNodeBegin, szFileName + i); pFileNode->Parent = Parent; // Also insert the node to the hash table so CascOpenFile can find it if(pFileNode->FileNameHash == 0) { pFileNode->FileNameHash = CalcNormNameHash(szPathBuffer, i); InsertToHashTable(pFileNode); } } return true; } */ size_t CASC_FILE_TREE::GetMaxFileIndex() { if(FileDataIds.IsInitialized()) { return FileDataIds.ItemCount(); } else { return NodeTable.ItemCount(); } } size_t CASC_FILE_TREE::GetCount() { return NodeTable.ItemCount(); } size_t CASC_FILE_TREE::IndexOf(PCASC_FILE_NODE pFileNode) { return NodeTable.IndexOf(pFileNode); } void CASC_FILE_TREE::GetExtras(PCASC_FILE_NODE pFileNode, PDWORD PtrFileDataId, PDWORD PtrLocaleFlags, PDWORD PtrContentFlags) { DWORD FileDataId = CASC_INVALID_ID; DWORD LocaleFlags = CASC_INVALID_ID; DWORD ContentFlags = CASC_INVALID_ID; // Retrieve the data ID, if supported if(PtrFileDataId != NULL) { if(FileDataIdOffset != 0) FileDataId = GET_NODE_INT32(pFileNode, FileDataIdOffset); PtrFileDataId[0] = FileDataId; } // Retrieve the locale ID, if supported if(PtrLocaleFlags != NULL) { if(LocaleFlagsOffset != 0) LocaleFlags = GET_NODE_INT32(pFileNode, LocaleFlagsOffset); PtrLocaleFlags[0] = LocaleFlags; } if(PtrContentFlags != NULL) { if(ContentFlagsOffset != 0) ContentFlags = GET_NODE_INT32(pFileNode, ContentFlagsOffset); PtrContentFlags[0] = ContentFlags; } } void CASC_FILE_TREE::SetExtras(PCASC_FILE_NODE pFileNode, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags) { // Set the file data ID, if supported if(FileDataIdOffset != 0) { SET_NODE_INT32(pFileNode, FileDataIdOffset, FileDataId); } // Set the locale ID, if supported if(LocaleFlagsOffset != 0) { SET_NODE_INT32(pFileNode, LocaleFlagsOffset, LocaleFlags); } // Set the locale ID, if supported if(ContentFlagsOffset != 0) { SET_NODE_INT32(pFileNode, ContentFlagsOffset, ContentFlags); } } ================================================ FILE: deps/CascLib/src/common/FileTree.h ================================================ /*****************************************************************************/ /* FileTree.h Copyright (c) Ladislav Zezula 2018 */ /*---------------------------------------------------------------------------*/ /* Common implementation of a file tree object for various ROOt file formats */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.05.18 1.00 Lad The first version of FileTree.h */ /*****************************************************************************/ #ifndef __FILETREE_H__ #define __FILETREE_H__ //----------------------------------------------------------------------------- // Structures #define FTREE_FLAG_USE_DATA_ID 0x0001 // The FILE_NODE also contains file data ID #define FTREE_FLAG_USE_LOCALE_FLAGS 0x0002 // The FILE_NODE also contains file locale flags #define FTREE_FLAG_USE_CONTENT_FLAGS 0x0004 // The FILE_NODE also contains content flags #define CFN_FLAG_FOLDER 0x0001 // This item is a folder #define CFN_FLAG_MOUNT_POINT 0x0002 // This item is a mount point. // Common structure for holding a single folder/file node typedef struct _CASC_FILE_NODE { ULONGLONG FileNameHash; // Jenkins hash of the normalized file name (uppercase, backslashes) PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the CKey entry DWORD Parent; // The index of a parent directory. If CASC_INVALID_INDEX, then this is the root item DWORD NameIndex; // Index of the node name. If CASC_INVALID_INDEX, then this node has no name USHORT NameLength; // Length of the node name (without the zero terminator) USHORT Flags; // See CFN_FLAG_XXX DWORD ExtraValues[4]; // FileDataId: Only if FTREE_FLAG_USE_DATA_ID specified at create // LocaleFlags: Only if FTREE_FLAG_USE_LOCALE_FLAGS specified at create // ContentFlags: Only if FTREE_FLAG_USE_CONTENT_FLAGS specified at create } CASC_FILE_NODE, *PCASC_FILE_NODE; // Main structure for the file tree class CASC_FILE_TREE { public: // Initializes/destroys the entire tree DWORD Create(DWORD Flags = 0); void Free(); // Inserts a new node to the tree; either with name or nameless PCASC_FILE_NODE InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId = CASC_INVALID_ID, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID); PCASC_FILE_NODE InsertByHash(PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG FileNameHash, DWORD FileDataId, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID); PCASC_FILE_NODE InsertById(PCASC_CKEY_ENTRY pCKeyEntry, DWORD FileDataId, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID); // Returns an item at the given index. The PathAt also builds the full path of the node PCASC_FILE_NODE ItemAt(size_t nItemIndex); PCASC_FILE_NODE PathAt(char * szBuffer, size_t cchBuffer, size_t nItemIndex); size_t PathAt(char * szBuffer, size_t cchBuffer, PCASC_FILE_NODE pFileNode); // Finds a file using its full path, FileDataId or CKey/EKey PCASC_FILE_NODE Find(const char * szFullPath, DWORD FileDataId, struct _CASC_FIND_DATA * pFindData); PCASC_FILE_NODE Find(PCASC_CKEY_ENTRY pCKeyEntry); PCASC_FILE_NODE Find(ULONGLONG FileNameHash); PCASC_FILE_NODE FindById(DWORD FileDataId); // Assigns a file name to the node bool SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName); // Returns the number of items in the tree size_t GetMaxFileIndex(); size_t GetCount(); // Returns the index of an item in the tree size_t IndexOf(PCASC_FILE_NODE pFileNode); // Retrieves the extra values from the node (if supported) void GetExtras(PCASC_FILE_NODE pFileNode, PDWORD PtrFileDataId, PDWORD PtrLocaleFlags, PDWORD PtrContentFlags); void SetExtras(PCASC_FILE_NODE pFileNode, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags); // Change the length of the key bool SetKeyLength(DWORD KeyLength); // Retrieve the maximum FileDataId ever inserted DWORD GetNextFileDataId(); protected: PCASC_FILE_NODE InsertNew(PCASC_CKEY_ENTRY pCKeyEntry); PCASC_FILE_NODE InsertNew(); bool InsertToHashTable(PCASC_FILE_NODE pFileNode); bool InsertToIdTable(PCASC_FILE_NODE pFileNode); bool SetNodePlainName(PCASC_FILE_NODE pFileNode, const char * szPlainName, const char * szPlainNameEnd); bool RebuildNameMaps(); CASC_ARRAY NodeTable; // Dynamic array that holds all CASC_FILE_NODEs CASC_ARRAY NameTable; // Dynamic array that holds all node names CASC_ARRAY FileDataIds; // Dynamic array that maps FileDataId -> CASC_FILE_NODE CASC_MAP NameMap; // Map of FileNameHash -> CASC_FILE_NODE size_t FileDataIdOffset; // If nonzero, this is the offset of the "FileDataId" field in the CASC_FILE_NODE size_t LocaleFlagsOffset; // If nonzero, this is the offset of the "LocaleFlags" field in the CASC_FILE_NODE size_t ContentFlagsOffset; // If nonzero, this is the offset of the "ContentFlags" field in the CASC_FILE_NODE size_t FolderNodes; // Number of folder nodes size_t FileNodes; // Number of file nodes DWORD KeyLength; // Actual length of the key supported by the root handler }; typedef CASC_FILE_TREE * PCASC_FILE_TREE; #endif // __FILETREE_H__ ================================================ FILE: deps/CascLib/src/common/IndexMap.h ================================================ /*****************************************************************************/ /* IndexMap.h Copyright (c) Ladislav Zezula 2019 */ /*---------------------------------------------------------------------------*/ /* Interface of index-based map for CascLib. This is a map type created */ /* on top of an array of contant-size objects, that is already sorted. */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.19 1.00 Lad The first version of Index.h */ /*****************************************************************************/ #ifndef __CASC_INDEX_MAP_H__ #define __CASC_INDEX_MAP_H__ //----------------------------------------------------------------------------- // Structures class CASC_INDEX_MAP { public: int Create(size_t MaxItems, size_t ObjectLength_, size_t KeyLength_, size_t KeyOffset_) { DWORD IndexBitSize = 8; // Determine the index size if(MaxItems >= 0x10000) IndexBitSize = 16; if(MaxItems >= 0x100000) IndexBitSize = 20; // Save the values IndexTableSize = (1 << IndexBitSize); IndexTableMask = IndexTableSize - 1; ObjectLength = ObjectLength_; KeyOffset = KeyOffset_; KeyLength = KeyLength_; // Allocate the new index object IndexTable = CASC_ALLOC(void *, IndexTableSize); if(IndexTable != NULL) { memset(IndexTable, 0, IndexTableSize * sizeof(void *)); return ERROR_SUCCESS; } return ERROR_NOT_ENOUGH_MEMORY; } void * FindObject(void * pvKey) { LPBYTE StartObject; DWORD StartIndex; // Verify pointer to the map if(IndexTable != NULL) { // Retrieve the index from the key StartIndex = KeyToIndex(pvKey); StartObject = (LPBYTE)IndexTable[StartIndex]; if(StartObject == NULL) return NULL; // Enumerate the array as long as the index matches do { // Compare the key if(!memcmp(StartObject + KeyOffset, pvKey, KeyLength)) return StartObject; // Key mismatch -> go next object StartObject = StartObject + ObjectLength; } while(KeyToIndex(StartObject + KeyOffset) == StartIndex); } // Not found return NULL; } protected: inline DWORD KeyToIndex(void * pvKey) { return *((PDWORD)pvKey) & IndexTableMask; } void ** IndexTable; // Pointer table. Each entry, if non-NULL, contains a pointer to the first object // into a sorted array. Search function must then match the entire key. // In case of no match, the search process must go to the next-in-array object. size_t ObjectLength; // Length of the single object size_t KeyOffset; // How far is the key from the begin of the structure (in bytes) size_t KeyLength; // Length of the key DWORD IndexTableSize; // Size of the IndexTableSize, in bytes DWORD IndexTableMask; // Bit mask for conversion from hash to index }; typedef CASC_INDEX_MAP *PCASC_INDEX_MAP; //----------------------------------------------------------------------------- // Functions bool IndexMap_InsertObject(PCASC_INDEX_MAP pIndexMap, void * pvNewObject, void * pvKey); void IndexMap_Free(PCASC_INDEX_MAP pIndexMap); #endif // __CASC_INDEX_MAP_H__ ================================================ FILE: deps/CascLib/src/common/ListFile.cpp ================================================ /*****************************************************************************/ /* ListFile.cpp Copyright (c) Ladislav Zezula 2004 */ /*---------------------------------------------------------------------------*/ /* Description: */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 12.06.04 1.00 Lad The first version of ListFile.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" //----------------------------------------------------------------------------- // Listfile cache structure #define LISTFILE_FLAG_USES_FILEDATAID 0x0001 // A new CSV format, containing FileDataId; FullFileName typedef struct _LISTFILE_CACHE { char * pBegin; // The begin of the listfile cache char * pPos; // Current position in the cache char * pEnd; // The last character in the file cache DWORD Flags; // Followed by the cache (variable length) } LISTFILE_CACHE, *PLISTFILE_CACHE; //----------------------------------------------------------------------------- // Creating the listfile cache for the given amount of data static PLISTFILE_CACHE ListFile_CreateCache(DWORD dwFileSize) { PLISTFILE_CACHE pCache; // Allocate cache for one file block pCache = (PLISTFILE_CACHE)CASC_ALLOC(sizeof(LISTFILE_CACHE) + dwFileSize); if(pCache != NULL) { // Set the initial pointers pCache->pBegin = pCache->pPos = (char *)(pCache + 1); pCache->pEnd = pCache->pBegin + dwFileSize; pCache->Flags = 0; } // Return the cache return pCache; } static char * ListFile_SkipSpaces(PLISTFILE_CACHE pCache) { // Skip newlines, spaces, tabs and another non-printable stuff while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20) pCache->pPos++; // Remember the begin of the line return pCache->pPos; } static void ListFile_CheckFormat(PLISTFILE_CACHE pCache) { // Only if the listfile is greatger than 2 MB if((pCache->pEnd - pCache->pBegin) > 0x100000) { char * szPtr = pCache->pBegin; size_t nDigitCount = 0; // Calculate the amount of digits while(nDigitCount <= 20 && '0' <= szPtr[nDigitCount] && szPtr[nDigitCount] <= '9') nDigitCount++; // There must be a semicolon after if(nDigitCount <= 10 && szPtr[nDigitCount] == ';') { pCache->Flags |= LISTFILE_FLAG_USES_FILEDATAID; } } } static int ListFile_GetFileDataId(PLISTFILE_CACHE pCache, PDWORD PtrFileDataId) { char * szLineBegin = ListFile_SkipSpaces(pCache); char * szLineEnd; DWORD dwNewInt32 = 0; DWORD dwInt32 = 0; // Set the limit for loading the number szLineEnd = CASCLIB_MIN((szLineBegin + 20), pCache->pEnd); // Extract decimal digits from the string while(szLineBegin < szLineEnd && '0' <= szLineBegin[0] && szLineBegin[0] <= '9') { // Check integer overflow dwNewInt32 = (dwInt32 * 10) + (szLineBegin[0] - '0'); if(dwNewInt32 < dwInt32) return ERROR_BAD_FORMAT; dwInt32 = dwNewInt32; szLineBegin++; } // There must still be some space if(szLineBegin < szLineEnd) { // There must be a semicolon after the decimal integer // The decimal integer must be smaller than 10 MB (files) if(szLineBegin[0] != ';' || dwInt32 >= 0xA00000) return ERROR_BAD_FORMAT; pCache->pPos = szLineBegin + 1; PtrFileDataId[0] = dwInt32; return ERROR_SUCCESS; } return ERROR_NO_MORE_FILES; } //----------------------------------------------------------------------------- // Functions for parsing an external listfile void * ListFile_OpenExternal(LPCTSTR szListFile) { PLISTFILE_CACHE pCache = NULL; TFileStream * pStream; ULONGLONG FileSize = 0; // Open the external listfile pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY); if(pStream != NULL) { // Retrieve the size of the external listfile FileStream_GetSize(pStream, &FileSize); if(0 < FileSize && FileSize <= 0x30000000) { // Create the in-memory cache for the entire listfile // The listfile does not have any data loaded yet pCache = ListFile_CreateCache((DWORD)FileSize); if(pCache != NULL) { if(FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize)) { ListFile_CheckFormat(pCache); } else { CASC_FREE(pCache); } } } // Close the file stream FileStream_Close(pStream); } return pCache; } void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer) { PLISTFILE_CACHE pCache = NULL; // Create the in-memory cache for the entire listfile // The listfile does not have any data loaded yet pCache = ListFile_CreateCache(cbBuffer); if(pCache != NULL) memcpy(pCache->pBegin, pbBuffer, cbBuffer); return pCache; } // Performs the MD5-based check on the listfile bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5) { PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile; // Must be at the beginning assert(pCache->pPos == pCache->pBegin); // Verify the MD5 hash for the entire block return CascVerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5); } size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd) { PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile; char * szExtraString = NULL; char * szLineBegin; char * szLineEnd; // Skip newlines, spaces, tabs and another non-printable stuff // Remember the begin of the line szLineBegin = ListFile_SkipSpaces(pCache); // Copy the remaining characters while(pCache->pPos < pCache->pEnd) { // If we have found a newline, stop loading // Note: the 0x85 char came from Overwatch build 24919 if(pCache->pPos[0] == '\x0A' || pCache->pPos[0] == '\x0D' || pCache->pPos[0] == '\x85') break; // Blizzard listfiles can also contain information about patch: // Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326) if(pCache->pPos[0] == '~') szExtraString = pCache->pPos; // Move the position by one character forward pCache->pPos++; } // Remember the end of the line szLineEnd = (szExtraString != NULL && szExtraString[0] == '~' && szExtraString[1] == 'P') ? szExtraString : pCache->pPos; // Give the caller the positions of the begin and end of the line pszLineBegin[0] = szLineBegin; pszLineEnd[0] = szLineEnd; return (size_t)(szLineEnd - szLineBegin); } size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars) { const char * szLineBegin = NULL; const char * szLineEnd = NULL; size_t nLength; DWORD dwErrCode = ERROR_SUCCESS; // Retrieve the next line nLength = ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd); // Check the length if(nLength < nMaxChars) { // Copy the line to the user buffer memcpy(szBuffer, szLineBegin, nLength); szBuffer[nLength] = 0; } else { dwErrCode = ERROR_INSUFFICIENT_BUFFER; nLength = 0; } // If we didn't read anything, set the error code if(nLength == 0) SetCascError(dwErrCode); return nLength; } size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PDWORD PtrFileDataId) { PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile; const char * szTemp; size_t nLength = 0; DWORD dwErrCode = ERROR_SUCCESS; // Check for parameters for(;;) { DWORD FileDataId = CASC_INVALID_ID; // If this is a CSV-format listfile, we need to extract the FileDataId // Lines that contain bogus data, invalid numbers or too big values will be skipped if(pCache->Flags & LISTFILE_FLAG_USES_FILEDATAID) { // Retrieve the data ID from the current position dwErrCode = ListFile_GetFileDataId(pCache, &FileDataId); if(dwErrCode == ERROR_NO_MORE_FILES) break; // If there was an error, skip the current line if(dwErrCode != ERROR_SUCCESS || FileDataId == CASC_INVALID_ID) { ListFile_GetNextLine(pvListFile, &szTemp, &szTemp); continue; } } // Read the (next) line nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars); if(nLength == 0) { dwErrCode = GetCascError(); break; } // Give the file data id and return true PtrFileDataId[0] = FileDataId; return nLength; } if(dwErrCode != ERROR_SUCCESS) SetCascError(dwErrCode); return nLength; } LPBYTE ListFile_GetData(void * pvListFile, PDWORD PtrDataSize) { PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile; LPBYTE pbData = NULL; DWORD cbData = 0; // Get data from the list file cache if (pvListFile != NULL) { pbData = (LPBYTE)pCache->pBegin; cbData = (DWORD)(pCache->pEnd - pCache->pBegin); } // Give the data to the caller if (PtrDataSize != NULL) PtrDataSize[0] = cbData; return pbData; } ================================================ FILE: deps/CascLib/src/common/ListFile.h ================================================ /*****************************************************************************/ /* ListFile.h Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Common functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 10.05.14 1.00 Lad The first version of ListFile.h */ /*****************************************************************************/ #ifndef __LISTFILE_H__ #define __LISTFILE_H__ //----------------------------------------------------------------------------- // Functions for parsing an external listfile void * ListFile_OpenExternal(LPCTSTR szListFile); void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer); bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5); size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd); size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars); size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PDWORD PtrFileDataId); LPBYTE ListFile_GetData(void * pvListFile, PDWORD PtrDataSize); #endif // __LISTFILE_H__ ================================================ FILE: deps/CascLib/src/common/Map.h ================================================ /*****************************************************************************/ /* Map.h Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* Interface of hash-to-ptr map for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 10.06.14 1.00 Lad The first version of Map.h */ /*****************************************************************************/ #ifndef __CASC_MAP_H__ #define __CASC_MAP_H__ //----------------------------------------------------------------------------- // Structures #define MIN_HASH_TABLE_SIZE 0x00000100 // The smallest size of the hash table. #define MAX_HASH_TABLE_SIZE 0x00800000 // The largest size of the hash table. Should be enough for any game. typedef int (*PFNCOMPAREFUNC)(const void * pvObjectKey, const void * pvKey, size_t nKeyLength); typedef DWORD (*PFNHASHFUNC)(void * pvKey, size_t nKeyLength); typedef enum _KEY_TYPE { KeyIsHash, // Use when the key is already a hash. If specified, the map will not hash the key KeyIsArbitrary, // The key is an arbitrary array of bytes. The map will hash it to get a map index KeyIsString // Use when the key is a string } KEY_TYPE, *PKEY_TYPE; #define KEY_LENGTH_STRING 0xFFFFFFFF //----------------------------------------------------------------------------- // Hashing functions // Used when the key is a hash already - no need to hash it again inline DWORD CalcHashValue_Hash(void * pvKey, size_t /* nKeyLength */) { // Get the hash directly as value return ConvertBytesToInteger_4((LPBYTE)pvKey); } // Calculates hash value from a key inline DWORD CalcHashValue_Key(void * pvKey, size_t nKeyLength) { LPBYTE pbKey = (LPBYTE)pvKey; DWORD dwHash = 0x7EEE7EEE; // Construct the hash from the key for(DWORD i = 0; i < nKeyLength; i++) dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[i]; // Return the hash limited by the table size return dwHash; } // Calculates hash value from a string inline DWORD CalcHashValue_String(const char * szString, const char * szStringEnd) { LPBYTE pbKeyEnd = (LPBYTE)szStringEnd; LPBYTE pbKey = (LPBYTE)szString; DWORD dwHash = 0x7EEE7EEE; // Hash the string itself while(pbKey < pbKeyEnd) { dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ AsciiToUpperTable_BkSlash[pbKey[0]]; pbKey++; } // Return the hash limited by the table size return dwHash; } //----------------------------------------------------------------------------- // Map implementation class CASC_MAP { public: CASC_MAP() { PfnCalcHashValue = NULL; m_HashTable = NULL; m_HashTableSize = 0; m_ItemCount = 0; m_KeyOffset = 0; m_KeyLength = 0; m_bKeyIsHash = false; } ~CASC_MAP() { Free(); } DWORD Create(size_t MaxItems, size_t KeyLength, size_t KeyOffset, KEY_TYPE KeyType = KeyIsHash) { // Set the class variables m_KeyLength = CASCLIB_MAX(KeyLength, 8); m_KeyOffset = KeyOffset; m_ItemCount = 0; // Setup the hashing function switch(KeyType) { case KeyIsHash: PfnCalcHashValue = CalcHashValue_Hash; break; case KeyIsArbitrary: PfnCalcHashValue = CalcHashValue_Key; break; case KeyIsString: PfnCalcHashValue = NULL; break; default: assert(false); return ERROR_NOT_SUPPORTED; } // Calculate the hash table size. Take 133% of the item count and round it up to the next power of two // This will make the hash table indexes somewhat more resilient against count changes and will make // e.g. file order in the file tree more stable. m_HashTableSize = GetNearestPowerOfTwo(MaxItems * 4 / 3); if(m_HashTableSize == 0) return ERROR_NOT_ENOUGH_MEMORY; // Allocate new map for the objects m_HashTable = (void **)CASC_ALLOC_ZERO(m_HashTableSize); return (m_HashTable != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; } void * FindObject(void * pvKey, PDWORD PtrIndex = NULL) { void * pvObject; DWORD dwHashIndex; // Verify pointer to the map if(m_HashTable != NULL) { // Construct the hash index dwHashIndex = HashToIndex(PfnCalcHashValue(pvKey, m_KeyLength)); // Search the hash table while((pvObject = m_HashTable[dwHashIndex]) != NULL) { // Compare the hash if(CompareObject_Key(pvObject, pvKey)) { if(PtrIndex != NULL) PtrIndex[0] = dwHashIndex; return pvObject; } // Move to the next entry dwHashIndex = HashToIndex(dwHashIndex + 1); } } // Not found, sorry return NULL; } bool InsertObject(void * pvNewObject, void * pvKey) { void * pvExistingObject; DWORD dwHashIndex; // Verify pointer to the map if(m_HashTable != NULL) { // Limit check if((m_ItemCount + 1) >= m_HashTableSize) return false; // Construct the hash index dwHashIndex = HashToIndex(PfnCalcHashValue(pvKey, m_KeyLength)); // Search the hash table while((pvExistingObject = m_HashTable[dwHashIndex]) != NULL) { // Check if hash being inserted conflicts with an existing hash if(CompareObject_Key(pvExistingObject, pvKey)) return false; // Move to the next entry dwHashIndex = HashToIndex(dwHashIndex + 1); } // Insert at that position m_HashTable[dwHashIndex] = pvNewObject; m_ItemCount++; return true; } // Failed return false; } const char * FindString(const char * szString, const char * szStringEnd) { const char * szExistingString; DWORD dwHashIndex; // Verify pointer to the map if(m_HashTable != NULL) { // Construct the main index dwHashIndex = HashToIndex(CalcHashValue_String(szString, szStringEnd)); // Search the hash table while((szExistingString = (const char *)m_HashTable[dwHashIndex]) != NULL) { // Compare the hash if(CompareObject_String(szExistingString, szString, szStringEnd)) return szExistingString; // Move to the next entry dwHashIndex = HashToIndex(dwHashIndex + 1); } } // Not found, sorry return NULL; } bool InsertString(const char * szString, bool bCutExtension) { const char * szExistingString; const char * szStringEnd = NULL; DWORD dwHashIndex; // Verify pointer to the map if(m_HashTable != NULL) { // Limit check if((m_ItemCount + 1) >= m_HashTableSize) return false; // Retrieve the length of the string without extension if(bCutExtension) szStringEnd = GetFileExtension(szString); else szStringEnd = szString + strlen(szString); // Construct the hash index dwHashIndex = HashToIndex(CalcHashValue_String(szString, szStringEnd)); // Search the hash table while((szExistingString = (const char *)m_HashTable[dwHashIndex]) != NULL) { // Check if hash being inserted conflicts with an existing hash if(CompareObject_String(szExistingString, szString, szStringEnd)) return false; // Move to the next entry dwHashIndex = HashToIndex(dwHashIndex + 1); } // Insert at that position m_HashTable[dwHashIndex] = (void *)szString; m_ItemCount++; return true; } // Failed return false; } void * ItemAt(size_t nIndex) { assert(nIndex < m_HashTableSize); return m_HashTable[nIndex]; } size_t HashTableSize() { return m_HashTableSize; } size_t ItemCount() { return m_ItemCount; } bool IsInitialized() { return (m_HashTable && m_HashTableSize); } void Free() { PfnCalcHashValue = NULL; CASC_FREE(m_HashTable); m_HashTableSize = 0; } protected: DWORD HashToIndex(DWORD HashValue) { return HashValue & (m_HashTableSize - 1); } bool CompareObject_Key(void * pvObject, void * pvKey) { LPBYTE pbObjectKey = (LPBYTE)pvObject + m_KeyOffset; return (memcmp(pbObjectKey, pvKey, m_KeyLength) == 0); } bool CompareObject_String(const char * szExistingString, const char * szString, const char * szStringEnd) { // Compare the whole part, case insensitive while(szString < szStringEnd) { if(AsciiToUpperTable_BkSlash[*szExistingString] != AsciiToUpperTable_BkSlash[*szString]) return false; szExistingString++; szString++; } return true; } size_t GetNearestPowerOfTwo(size_t MaxItems) { size_t PowerOfTwo; // Round the hash table size up to the nearest power of two for(PowerOfTwo = MIN_HASH_TABLE_SIZE; PowerOfTwo <= MAX_HASH_TABLE_SIZE; PowerOfTwo <<= 1) { if(PowerOfTwo > MaxItems) { return PowerOfTwo; } } // If the hash table is too big, we cannot create the map assert(false); return 0; } PFNHASHFUNC PfnCalcHashValue; void ** m_HashTable; // Hash table size_t m_HashTableSize; // Size of the hash table, in entries. Always a power of two. size_t m_ItemCount; // Number of objects in the map size_t m_KeyOffset; // How far is the hash from the begin of the objects (in bytes) size_t m_KeyLength; // Length of the hash key, in bytes bool m_bKeyIsHash; // If set, then it means that the key is a hash of some sort. // Will improve performance, as we will not hash a hash :-) }; //----------------------------------------------------------------------------- // Key map interface // Maximum length of encryption key #define CASC_KEY_LENGTH 0x10 #define CASC_KEY_TABLE_SIZE 0x100 #define CASC_KEY_TABLE_MASK (CASC_KEY_TABLE_SIZE - 1) class CASC_KEY_MAP { public: CASC_KEY_MAP(); ~CASC_KEY_MAP(); LPBYTE FindKey(ULONGLONG KeyName); bool AddKey(ULONGLONG KeyName, LPBYTE Key); protected: void * HashTable[CASC_KEY_TABLE_SIZE]; }; #endif // __CASC_MAP_H__ ================================================ FILE: deps/CascLib/src/common/Mime.cpp ================================================ /*****************************************************************************/ /* Mime.cpp Copyright (c) Ladislav Zezula 2021 */ /*---------------------------------------------------------------------------*/ /* Mime functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 21.01.21 1.00 Lad Created */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" //----------------------------------------------------------------------------- // Local variables #define BASE64_INVALID_CHAR 0xFF #define BASE64_WHITESPACE_CHAR 0xFE static const char * CascBase64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static unsigned char CascBase64ToBits[0x80] = {0}; //----------------------------------------------------------------------------- // CASC_MIME_HTTP implementation static size_t DecodeValueInt32(const char * string, const char * string_end) { size_t result = 0; while(string < string_end && isdigit(string[0])) { result = (result * 10) + (string[0] - '0'); string++; } return result; } bool CASC_MIME_HTTP::IsDataComplete(const char * response, size_t response_length) { const char * content_length_ptr; const char * content_begin_ptr; // Do not parse the HTTP response multiple times if(response_valid == 0 && response_length > 8) { // Check the begin of the response if(!strncmp(response, "HTTP/1.1", 8)) { // Check if there's begin of the content if((content_begin_ptr = strstr(response, "\r\n\r\n")) != NULL) { // HTTP responses contain "Content-Length: %u\n\r" if((content_length_ptr = strstr(response, "Content-Length: ")) != NULL) { // The content length info muse be before the actual content if(content_length_ptr < content_begin_ptr) { // Fill the HTTP info cache response_valid = 0x48545450; // 'HTTP' content_offset = (content_begin_ptr + 4) - response; content_length = DecodeValueInt32(content_length_ptr + 16, content_begin_ptr); total_length = content_offset + content_length; } } } } } // If we know the expected total length, we can tell whether it's complete or not return ((response_valid != 0) && (total_length == response_length)); } //----------------------------------------------------------------------------- // The MIME blob class CASC_MIME_BLOB::CASC_MIME_BLOB(char * mime_ptr, char * mime_end) { ptr = mime_ptr; end = mime_end; } CASC_MIME_BLOB::~CASC_MIME_BLOB() { ptr = end = NULL; } char * CASC_MIME_BLOB::GetNextLine() { char * mime_line = ptr; while(ptr < end) { // Every line, even the last one, must be terminated with 0D 0A if(ptr[0] == 0x0D && ptr[1] == 0x0A) { // If space or tabulator follows, then this is continuation of the line if(ptr[2] == 0x09 || ptr[2] == 0x20) { ptr = ptr + 2; continue; } // Terminate the line and return its begin ptr[0] = 0; ptr[1] = 0; ptr = ptr + 2; return mime_line; } // Move to tne next character ptr++; } // No EOL terminated line found, break the search return NULL; } //----------------------------------------------------------------------------- // The MIME element class CASC_MIME_ELEMENT::CASC_MIME_ELEMENT() { memset(this, 0, sizeof(CASC_MIME_ELEMENT)); } CASC_MIME_ELEMENT::~CASC_MIME_ELEMENT() { // Free the children and next elements if(folder.pChild != NULL) delete folder.pChild; folder.pChild = NULL; if(folder.pNext != NULL) delete folder.pNext; folder.pNext = NULL; // Free the data if(data.begin != NULL) CASC_FREE(data.begin); data.begin = NULL; } unsigned char * CASC_MIME_ELEMENT::GiveAway(size_t * ptr_data_length) { unsigned char * give_away_data = data.begin; size_t give_away_length = data.length; // Clear the data (DO NOT FREE) data.begin = NULL; data.length = 0; // Copy the data to local buffer if(ptr_data_length != NULL) ptr_data_length[0] = give_away_length; return give_away_data; } DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, const char * boundary_ptr) { CASC_MIME_ENCODING Encoding = MimeEncodingTextPlain; CASC_MIME_BLOB mime_data(mime_data_begin, mime_data_end); CASC_MIME_HTTP HttpInfo; size_t length_begin; size_t length_end; char * mime_line; char boundary_begin[MAX_LENGTH_BOUNDARY + 2]; char boundary_end[MAX_LENGTH_BOUNDARY + 4]; DWORD dwErrCode = ERROR_SUCCESS; bool mime_version = false; // Diversion for HTTP: No need to parse the entire headers and stuff. // Just give the data right away if(HttpInfo.IsDataComplete(mime_data_begin, (mime_data_end - mime_data_begin))) { if((data.begin = CASC_ALLOC(HttpInfo.content_length)) == NULL) return ERROR_NOT_ENOUGH_MEMORY; memcpy(data.begin, mime_data_begin + HttpInfo.content_offset, HttpInfo.content_length); data.length = HttpInfo.content_length; return ERROR_SUCCESS; } // Reset the boundary boundary[0] = 0; // Parse line-by-line untile we find the end of data while((mime_line = mime_data.GetNextLine()) != NULL) { // If the line is empty, this means that it's the end of the MIME header and begin of the MIME data if(mime_line[0] == 0) { mime_data.ptr = mime_line + 2; break; } // Root nodes are required to have MIME version if(boundary_ptr == NULL && mime_version == false) { if(!strcmp(mime_line, "MIME-Version: 1.0")) { mime_version = true; continue; } if(!strcmp(mime_line, "HTTP/1.1 200 OK")) { mime_version = true; continue; } } // Get the encoding if(!strncmp(mime_line, "Content-Transfer-Encoding: ", 27)) { ExtractEncoding(mime_line + 27, Encoding); continue; } // Is there content type? if(!strncmp(mime_line, "Content-Type: ", 14)) { ExtractBoundary(mime_line + 14); continue; } } // Keep going only if we have MIME version if(boundary_ptr != NULL || mime_version == true) { // If we have boundary, it means that the element begin // and end is marked by the boundary if(boundary[0]) { CASC_MIME_ELEMENT * pLast = NULL; CASC_MIME_ELEMENT * pChild; CASC_MIME_BLOB sub_mime(mime_data.ptr, NULL); // Construct the begin of the boundary. Don't include newline there, // as it must also match end of the boundary length_begin = CascStrPrintf(boundary_begin, _countof(boundary_begin), "--%s", boundary); dwErrCode = ERROR_SUCCESS; // The current line must point to the begin of the boundary // Find the end of the boundary if(memcmp(sub_mime.ptr, boundary_begin, length_begin)) return ERROR_BAD_FORMAT; sub_mime.ptr += length_begin; // Find the end of the boundary length_end = CascStrPrintf(boundary_end, _countof(boundary_end), "--%s--\r\n", boundary); sub_mime.end = strstr(sub_mime.ptr, boundary_end); if(sub_mime.end == NULL) return ERROR_BAD_FORMAT; // This is the end of the MIME section. Cut it to blocks // and put each into the separate CASC_MIME_ELEMENT while(sub_mime.ptr < sub_mime.end) { char * sub_mime_next; // At this point, there must be newline in the current data pointer if(sub_mime.ptr[0] != 0x0D || sub_mime.ptr[1] != 0x0A) return ERROR_BAD_FORMAT; sub_mime.ptr += 2; // Find the next MIME element. This must succeed, as the last element also matches the first element sub_mime_next = strstr(sub_mime.ptr, boundary_begin); if(sub_mime_next == NULL) return ERROR_BAD_FORMAT; // Allocate the element pChild = AllocateAndLoadElement(sub_mime.ptr, sub_mime_next - 2, boundary_begin); if(pChild == NULL) { dwErrCode = GetCascError(); break; } // Link the element if(folder.pChild == NULL) folder.pChild = pChild; if(pLast != NULL) pLast->folder.pNext = pChild; pLast = pChild; // Move to the next MIME element. Note that if we are at the ending boundary, // this moves past the end, but it's OK, because the while loop will catch that sub_mime.ptr = sub_mime_next + length_begin; } } else { CASC_MIME_BLOB content(mime_data.ptr, NULL); unsigned char * data_buffer; size_t data_length = 0; size_t raw_length; // If we have boundary pointer, we need to cut the data up to the boundary end. // Otherwise, we decode the data to the end of the document if(boundary_ptr != NULL) { // Find the end of the current data by the boundary. It is 2 characters before the next boundary content.end = strstr(content.ptr, boundary_ptr); if(content.end == NULL) return ERROR_BAD_FORMAT; if((content.ptr + 2) >= content.end) return ERROR_BAD_FORMAT; content.end -= 2; } else { content.end = mime_data_end - 2; if(content.end[0] != 0x0D || content.end[1] != 0x0A) return ERROR_BAD_FORMAT; if((content.ptr + 2) >= content.end) return ERROR_BAD_FORMAT; } // Allocate buffer for decoded data. // Make it the same size like source data plus zero at the end raw_length = (content.end - content.ptr); data_buffer = CASC_ALLOC(raw_length); if(data_buffer != NULL) { // Decode the data switch(Encoding) { case MimeEncodingTextPlain: dwErrCode = DecodeTextPlain(content.ptr, content.end, data_buffer, &data_length); break; case MimeEncodingQuotedPrintable: dwErrCode = DecodeQuotedPrintable(content.ptr, content.end, data_buffer, &data_length); break; case MimeEncodingBase64: dwErrCode = DecodeBase64(content.ptr, content.end, data_buffer, &data_length); break; default:; // to remove warning } // If failed, free the buffer back if(dwErrCode != ERROR_SUCCESS) { CASC_FREE(data_buffer); data_buffer = NULL; data_length = 0; } } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Put the data there, even if they are invalid data.begin = data_buffer; data.length = data_length; } } else { dwErrCode = ERROR_NOT_SUPPORTED; } // Return the result of the decoding return dwErrCode; } #ifdef _DEBUG #define MAX_LEVEL 0x10 void CASC_MIME_ELEMENT::Print(size_t nLevel, size_t nIndex) { char Prefix[(MAX_LEVEL * 4) + 0x20 + 1] = {0}; size_t nSpaces; // Fill-in the spaces nSpaces = (nLevel < MAX_LEVEL) ? (nLevel * 4) : (MAX_LEVEL * 4); memset(Prefix, ' ', nSpaces); // Print the spaces and index nSpaces = printf("%s* [%u]: ", Prefix, (int)nIndex); memset(Prefix, ' ', nSpaces); // Is this a folder item? if(folder.pChild != NULL) { printf("Folder item (boundary: %s)\n", boundary); folder.pChild->Print(nLevel + 1, 0); } else { char data_printable[0x20] = {0}; for(size_t i = 0; (i < data.length && i < _countof(data_printable) - 1); i++) { if(0x20 <= data.begin[i] && data.begin[i] <= 0x7F) data_printable[i] = data.begin[i]; else data_printable[i] = '.'; } printf("Data item (%u bytes): \"%s\"\n", (int)data.length, data_printable); } // Do we have a next element? if(folder.pNext != NULL) { folder.pNext->Print(nLevel, nIndex + 1); } } #endif CASC_MIME_ELEMENT * CASC_MIME_ELEMENT::AllocateAndLoadElement(char * a_mime_data, char * a_mime_data_end, const char * boundary_begin) { CASC_MIME_ELEMENT * pElement; DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Allocate the element if((pElement = new CASC_MIME_ELEMENT()) != NULL) { // Load the element dwErrCode = pElement->Load(a_mime_data, a_mime_data_end, boundary_begin); if(dwErrCode == ERROR_SUCCESS) return pElement; // Free the element on failure delete pElement; } SetCascError(dwErrCode); return NULL; } bool CASC_MIME_ELEMENT::ExtractEncoding(const char * line, CASC_MIME_ENCODING & Encoding) { if(!_stricmp(line, "base64")) { Encoding = MimeEncodingBase64; return true; } if(!_stricmp(line, "quoted-printable")) { Encoding = MimeEncodingQuotedPrintable; return true; } // Unknown encoding return false; } bool CASC_MIME_ELEMENT::ExtractBoundary(const char * line) { const char * begin; const char * end; // Find the begin of the boundary if((begin = strstr(line, "boundary=\"")) != NULL) { // Set begin of the boundary begin = begin + 10; // Is there also end? if((end = strchr(begin, '\"')) != NULL) { CascStrCopy(boundary, _countof(boundary), begin, end - begin); return true; } } return false; } DWORD CASC_MIME_ELEMENT::DecodeTextPlain(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length) { size_t data_length = (size_t)(content_end - content_begin); // Sanity checks assert(content_begin && content_end); assert(content_end > content_begin); // Plain copy memcpy(data_buffer, content_begin, data_length); // Give the result if(ptr_length != NULL) ptr_length[0] = data_length; return ERROR_SUCCESS; } DWORD CASC_MIME_ELEMENT::DecodeQuotedPrintable(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length) { unsigned char * save_data_buffer = data_buffer; char * content_ptr; DWORD dwErrCode; // Sanity checks assert(content_begin && content_end); assert(content_end > content_begin); // Decode the data for(content_ptr = content_begin; content_ptr < content_end; ) { // If the data begins with '=', there is either newline or 2-char hexa number if(content_ptr[0] == '=') { // Is there a newline after the equal sign? if(content_ptr[1] == 0x0D && content_ptr[2] == 0x0A) { content_ptr += 3; continue; } // Is there hexa number after the equal sign? dwErrCode = BinaryFromString(content_ptr + 1, 2, data_buffer); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; content_ptr += 3; data_buffer++; continue; } else { *data_buffer++ = (unsigned char)(*content_ptr++); } } if(ptr_length != NULL) ptr_length[0] = (size_t)(data_buffer - save_data_buffer); return ERROR_SUCCESS; } DWORD CASC_MIME_ELEMENT::DecodeBase64(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length) { unsigned char * save_data_buffer = data_buffer; DWORD BitBuffer = 0; DWORD BitCount = 0; BYTE OneByte; // One time preparation of the conversion table if(CascBase64ToBits[0] == 0) { // Fill the entire table with 0xFF to mark invalid characters memset(CascBase64ToBits, BASE64_INVALID_CHAR, sizeof(CascBase64ToBits)); // Set all whitespace characters for(BYTE i = 1; i <= 0x20; i++) CascBase64ToBits[i] = BASE64_WHITESPACE_CHAR; // Set all valid characters for(BYTE i = 0; CascBase64Table[i] != 0; i++) { OneByte = CascBase64Table[i]; CascBase64ToBits[OneByte] = i; } } // Do the decoding while(content_begin < content_end && content_begin[0] != '=') { // Check for end of string if(content_begin[0] > sizeof(CascBase64ToBits)) return ERROR_BAD_FORMAT; if((OneByte = CascBase64ToBits[*content_begin++]) == BASE64_INVALID_CHAR) return ERROR_BAD_FORMAT; if(OneByte == BASE64_WHITESPACE_CHAR) continue; // Put the 6 bits into the bit buffer BitBuffer = (BitBuffer << 6) | OneByte; BitCount += 6; // Flush all values while(BitCount >= 8) { // Decrement the bit count in the bit buffer BitCount -= 8; // The byte is the upper 8 bits of the bit buffer OneByte = (BYTE)(BitBuffer >> BitCount); BitBuffer = BitBuffer % (1 << BitCount); // Put the byte value. The buffer can not overflow, // because it is guaranteed to be equal to the length of the base64 string *data_buffer++ = OneByte; } } // Return the decoded length if(ptr_length != NULL) ptr_length[0] = (data_buffer - save_data_buffer); return ERROR_SUCCESS; } //----------------------------------------------------------------------------- // The MIME class CASC_MIME::CASC_MIME() {} CASC_MIME::~CASC_MIME() {} unsigned char * CASC_MIME::GiveAway(size_t * ptr_data_length) { CASC_MIME_ELEMENT * pElement = &root; unsigned char * data; // 1) Give the data from the root if((data = root.GiveAway(ptr_data_length)) != NULL) return data; // 2) If we have children, then give away from the first child pElement = root.GetChild(); if(pElement && (data = pElement->GiveAway(ptr_data_length)) != NULL) return data; // Return NULL if(ptr_data_length != NULL) ptr_data_length[0] = 0; return NULL; } DWORD CASC_MIME::Load(char * data, size_t length) { // Clear the root element memset(&root, 0, sizeof(CASC_MIME_ELEMENT)); //FILE * fp = fopen("E:\\html_response.txt", "wb"); //if(fp != NULL) //{ // fwrite(data, 1, length, fp); // fclose(fp); //} // Load the root element return root.Load(data, data + length); } DWORD CASC_MIME::Load(LPCTSTR szFileName) { char * szFileData; DWORD cbFileData = 0; DWORD dwErrCode = ERROR_SUCCESS; // Note that LoadFileToMemory allocated one byte more and puts zero at the end // Thus, we can treat it as zero-terminated string szFileData = (char *)LoadFileToMemory(szFileName, &cbFileData); if(szFileData != NULL) { dwErrCode = Load(szFileData, cbFileData); CASC_FREE(szFileData); } else { dwErrCode = GetCascError(); } return dwErrCode; } #ifdef _DEBUG void CASC_MIME::Print() { root.Print(0, 0); } #endif ================================================ FILE: deps/CascLib/src/common/Mime.h ================================================ /*****************************************************************************/ /* Mime.h Copyright (c) Ladislav Zezula 2021 */ /*---------------------------------------------------------------------------*/ /* MIME parsing functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 21.01.21 1.00 Lad Created */ /*****************************************************************************/ #ifndef __MIME_H__ #define __MIME_H__ //----------------------------------------------------------------------------- // MIME constants #define MAX_LENGTH_BOUNDARY 128 enum CASC_MIME_ENCODING { MimeEncodingTextPlain, MimeEncodingBase64, MimeEncodingQuotedPrintable, MimeEncodingMax }; //----------------------------------------------------------------------------- // Structure for caching parsed HTTP response information struct CASC_MIME_HTTP { CASC_MIME_HTTP() { response_valid = content_length = content_offset = total_length = 0; } bool IsDataComplete(const char * response, size_t response_length); size_t response_valid; // Nonzero if this is an already parsed HTTP response size_t content_length; // Parsed value of "Content-Length" size_t content_offset; // Offset of the HTTP data, relative to the begin of the response size_t total_length; // Expected total length of the HTTP response (content_offset + content_size) }; //----------------------------------------------------------------------------- // MIME blob class struct CASC_MIME_BLOB { CASC_MIME_BLOB(char * mime_ptr, char * mime_end); ~CASC_MIME_BLOB(); char * GetNextLine(); char * ptr; char * end; }; //----------------------------------------------------------------------------- // MIME class class CASC_MIME_ELEMENT { public: CASC_MIME_ELEMENT(); ~CASC_MIME_ELEMENT(); unsigned char * GiveAway(size_t * ptr_data_length); DWORD Load(char * mime_data_begin, char * mime_data_end, const char * boundary_ptr = NULL); CASC_MIME_ELEMENT * GetChild() { return folder.pChild; } #ifdef _DEBUG void Print(size_t nLevel, size_t nIndex); #endif protected: CASC_MIME_ELEMENT * AllocateAndLoadElement(char * a_mime_data, char * a_mime_data_end, const char * boundary_begin); bool ExtractEncoding(const char * line, CASC_MIME_ENCODING & Encoding); bool ExtractBoundary(const char * line); DWORD DecodeTextPlain(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length); DWORD DecodeQuotedPrintable(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length); DWORD DecodeBase64(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length); struct { CASC_MIME_ELEMENT * pChild; // Pointer to the first child CASC_MIME_ELEMENT * pNext; // Pointer to the next-in-folder element } folder; struct { unsigned char * begin; size_t length; } data; char boundary[MAX_LENGTH_BOUNDARY]; }; class CASC_MIME { public: CASC_MIME(); ~CASC_MIME(); unsigned char * GiveAway(size_t * ptr_data_length); DWORD Load(char * data, size_t length); DWORD Load(LPCTSTR fileName); #ifdef _DEBUG void Print(); #endif protected: CASC_MIME_ELEMENT root; }; //----------------------------------------------------------------------------- // HTTP helpers bool IsHttpResponseComplete(CASC_MIME_HTTP & HttpInfo, const char * response, size_t response_length); #endif // __MIME_H__ ================================================ FILE: deps/CascLib/src/common/Path.h ================================================ /*****************************************************************************/ /* Path.h Copyright (c) Ladislav Zezula 2019 */ /*---------------------------------------------------------------------------*/ /* Global path merger class */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 24.07.17 1.00 Lad Created */ /*****************************************************************************/ #ifndef __CASC_PATH_H__ #define __CASC_PATH_H__ //----------------------------------------------------------------------------- // Structures template struct CASC_PATH { CASC_PATH(int chSeparator = PATH_SEP_CHAR) { m_szBufferBegin = m_szBufferPtr = m_Buffer; m_szBufferEnd = m_szBufferBegin + _countof(m_Buffer); m_chSeparator = (xchar)chSeparator; m_Buffer[0] = 0; } ~CASC_PATH() { if(m_szBufferBegin != m_Buffer) { CASC_FREE(m_szBufferBegin); } } // LPCTSTR szPath = Path; operator const xchar *() { return m_szBufferBegin; } // LPTSTR szPath = Path.New(); xchar * New() { xchar * szNewStr; if((szNewStr = CASC_ALLOC(Length() + 1)) != NULL) { memcpy(szNewStr, m_szBufferBegin, Length() * sizeof(xchar)); szNewStr[Length()] = 0; } return szNewStr; } size_t Save() { return (m_szBufferPtr - m_szBufferBegin); } bool Restore(size_t nSavePos) { if((m_szBufferBegin + nSavePos) < m_szBufferEnd) { m_szBufferPtr = m_szBufferBegin + nSavePos; m_szBufferPtr[0] = 0; return true; } return false; } // Path.Copy(szBuffer, _countof(szBuffer)); bool Copy(xchar * szBuffer, size_t cchBuffer) { if((Length() + 1) > cchBuffer) return false; memcpy(szBuffer, m_szBufferBegin, Length() * sizeof(xchar)); szBuffer[Length()] = 0; return true; } size_t Length() { return m_szBufferPtr - m_szBufferBegin; } bool SetPathRoot(const xchar * szRoot) { // Make sure that there is no characters m_szBufferPtr = m_szBufferBegin; m_szBufferPtr[0] = 0; // Append the root path return AppendString(szRoot, false); } bool AppendStringN(const xchar * szString, size_t nMaxchars, bool bWithSeparator) { const xchar * szStringEnd = szString + nMaxchars; xchar chOneChar; if(szString && szString[0] && nMaxchars) { // Append separator, if required and not in begin of the string if(m_szBufferPtr > m_szBufferBegin && bWithSeparator) AppendChar(m_chSeparator); // Append the characters from the string while(szString[0] && szString < szStringEnd) { // Retrieve the single character chOneChar = *szString++; // Normalize the character if(chOneChar == '/' || chOneChar == '\\') chOneChar = m_chSeparator; if(!AppendChar(chOneChar)) break; } } return true; } bool AppendString(const xchar * szString, bool bWithSeparator) { return AppendStringN(szString, (0x10000 / sizeof(xchar)), bWithSeparator); } bool AppendEKey(LPBYTE pbEKey) { xchar szEKey[MD5_STRING_SIZE + 1]; StringFromBinary(pbEKey, MD5_HASH_SIZE, szEKey); AppendStringN(szEKey, 2, true); AppendStringN(szEKey+2, 2, true); return AppendString(szEKey, true); } bool AppendChar(xchar chOneChar) { // Buffer our of space? if((m_szBufferPtr + 2) >= m_szBufferEnd) { xchar * szOldBuffer = m_szBufferBegin; xchar * szNewBuffer; size_t nToAllocate = (m_szBufferEnd - m_szBufferBegin) * 2; size_t nLength = (m_szBufferPtr - m_szBufferBegin); if((szNewBuffer = CASC_ALLOC(nToAllocate)) == NULL) return false; // Copy the chars memcpy(szNewBuffer, m_szBufferBegin, (m_szBufferPtr - m_szBufferBegin) * sizeof(xchar)); m_szBufferBegin = szNewBuffer; m_szBufferPtr = m_szBufferBegin + nLength; m_szBufferEnd = m_szBufferBegin + nToAllocate; // Free the old buffer if(szOldBuffer != m_Buffer) CASC_FREE(szOldBuffer); } // Append the character *m_szBufferPtr++ = chOneChar; m_szBufferPtr[0] = 0; return true; } protected: xchar * m_szBufferBegin; xchar * m_szBufferPtr; xchar * m_szBufferEnd; xchar m_Buffer[MAX_PATH]; xchar m_chSeparator; }; #endif // __CASC_PATH_H__ ================================================ FILE: deps/CascLib/src/common/RootHandler.cpp ================================================ /*****************************************************************************/ /* RootHandler.cpp Copyright (c) Ladislav Zezula 2015 */ /*---------------------------------------------------------------------------*/ /* Implementation of root handler */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 09.03.15 1.00 Lad Created */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" //----------------------------------------------------------------------------- // Constructor and destructor - TFileTreeRoot TFileTreeRoot::TFileTreeRoot(DWORD FileTreeFlags) : TRootHandler() { // Initialize the file tree FileTree.Create(FileTreeFlags); } TFileTreeRoot::~TFileTreeRoot() { // Free the file tree FileTree.Free(); dwFeatures = 0; } //----------------------------------------------------------------------------- // Virtual functions - TFileTreeRoot int TFileTreeRoot::Insert( const char * szFileName, PCASC_CKEY_ENTRY pCKeyEntry) { PCASC_FILE_NODE pFileNode; pFileNode = FileTree.InsertByName(pCKeyEntry, szFileName, FileTree.GetNextFileDataId()); return (pFileNode != NULL) ? ERROR_SUCCESS : ERROR_CAN_NOT_COMPLETE; } PCASC_CKEY_ENTRY TFileTreeRoot::GetFile(TCascStorage * /* hs */, const char * szFileName) { PCASC_FILE_NODE pFileNode; ULONGLONG FileNameHash = CalcFileNameHash(szFileName); pFileNode = FileTree.Find(FileNameHash); return (pFileNode != NULL) ? pFileNode->pCKeyEntry : NULL; } PCASC_CKEY_ENTRY TFileTreeRoot::GetFile(TCascStorage * /* hs */, DWORD FileDataId) { PCASC_FILE_NODE pFileNode; pFileNode = FileTree.FindById(FileDataId); return (pFileNode != NULL) ? pFileNode->pCKeyEntry : NULL; } PCASC_CKEY_ENTRY TFileTreeRoot::Search(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) { PCASC_FILE_NODE pFileNode; size_t nMaxFileIndex = FileTree.GetMaxFileIndex(); // Are we still inside the root directory range? while(pSearch->nFileIndex < nMaxFileIndex) { //BREAKIF(pSearch->nFileIndex >= 2823765); // Retrieve the file item pFileNode = FileTree.PathAt(pFindData->szFileName, MAX_PATH, pSearch->nFileIndex++); if(pFileNode != NULL) { // Ignore folders and mount points if(!(pFileNode->Flags & CFN_FLAG_FOLDER)) { // Check the wildcard if (CascCheckWildCard(pFindData->szFileName, pSearch->szMask)) { // Retrieve the extra values (FileDataId, file size and locale flags) FileTree.GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags); // Return the found CKey entry return pFileNode->pCKeyEntry; } } } } // No more entries return NULL; } bool TFileTreeRoot::GetInfo(PCASC_CKEY_ENTRY pCKeyEntry, PCASC_FILE_FULL_INFO pFileInfo) { PCASC_FILE_NODE pFileNode; // Can't do much if the root key is NULL if(pCKeyEntry != NULL) { pFileNode = FileTree.Find(pCKeyEntry); if(pFileNode != NULL) { FileTree.GetExtras(pFileNode, &pFileInfo->FileDataId, &pFileInfo->LocaleFlags, &pFileInfo->ContentFlags); pFileInfo->FileNameHash = pFileNode->FileNameHash; return true; } } return false; } ================================================ FILE: deps/CascLib/src/common/RootHandler.h ================================================ /*****************************************************************************/ /* RootHandler.h Copyright (c) Ladislav Zezula 2015 */ /*---------------------------------------------------------------------------*/ /* Interface for root handlers */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 09.03.15 1.00 Lad Created */ /*****************************************************************************/ #ifndef __ROOT_HANDLER_H__ #define __ROOT_HANDLER_H__ //----------------------------------------------------------------------------- // Defines #define CASC_MNDX_ROOT_SIGNATURE 0x58444E4D // 'MNDX' #define CASC_TVFS_ROOT_SIGNATURE 0x53465654 // 'TVFS' #define CASC_DIABLO3_ROOT_SIGNATURE 0x8007D0C4 #define CASC_WOW82_ROOT_SIGNATURE 0x4D465354 // 'TSFM', WoW since 8.2 #define DUMP_LEVEL_ROOT_FILE 1 // Dump root file #define DUMP_LEVEL_ENCODING_FILE 2 // Dump root file + encoding file #define DUMP_LEVEL_INDEX_ENTRIES 3 // Dump root file + encoding file + index entries //----------------------------------------------------------------------------- // Class for generic root handler struct TRootHandler { public: TRootHandler() { dwFeatures = 0; } virtual ~TRootHandler() {} // Inserts new file name to the root handler // szFileName - Pointer to the file name // pCKeyEntry - Pointer to the CASC_CKEY_ENTRY for the file virtual int Insert(const char * /* szFileName */, PCASC_CKEY_ENTRY /* pCKeyEntry */) { return ERROR_NOT_SUPPORTED; } // Searches the file by file name // hs - Pointer to the storage structure // szFileName - Pointer to the file name virtual PCASC_CKEY_ENTRY GetFile(struct TCascStorage * /* hs */, const char * /* szFileName */) { return NULL; } // Searches the file by file data id // hs - Pointer to the storage structure // FileDataId - File data id virtual PCASC_CKEY_ENTRY GetFile(struct TCascStorage * /* hs */, DWORD /* FileDataId */) { return NULL; } // Performs find-next-file operation // pSearch - Pointer to the initialized search structure // pFindData - Pointer to output structure that will contain the information virtual PCASC_CKEY_ENTRY Search(struct TCascSearch * /* pSearch */, struct _CASC_FIND_DATA * /* pFindData */) { return NULL; } // Returns advanced info from the root file entry. // pCKeyEntry - CKey/EKey, depending on which type the root handler provides // pFileInfo - Pointer to CASC_FILE_FULL_INFO structure virtual bool GetInfo(PCASC_CKEY_ENTRY /* pCKeyEntry */, struct _CASC_FILE_FULL_INFO * /* pFileInfo */) { return false; } DWORD GetFeatures() { return dwFeatures; } protected: DWORD dwFeatures; // CASC features. See CASC_FEATURE_XXX }; //----------------------------------------------------------------------------- // Class for root handler that has basic mapping of FileName -> CASC_FILE_NODE struct TFileTreeRoot : public TRootHandler { TFileTreeRoot(DWORD FileTreeFlags); virtual ~TFileTreeRoot(); int Insert(const char * szFileName, PCASC_CKEY_ENTRY pCKeyEntry); PCASC_CKEY_ENTRY GetFile(struct TCascStorage * hs, const char * szFileName); PCASC_CKEY_ENTRY GetFile(struct TCascStorage * hs, DWORD FileDataId); PCASC_CKEY_ENTRY Search(struct TCascSearch * pSearch, struct _CASC_FIND_DATA * pFindData); bool GetInfo(PCASC_CKEY_ENTRY pCKeyEntry, struct _CASC_FILE_FULL_INFO * pFileInfo); protected: CASC_FILE_TREE FileTree; }; #endif // __ROOT_HANDLER_H__ ================================================ FILE: deps/CascLib/src/common/Sockets.cpp ================================================ /*****************************************************************************/ /* Sockets.cpp Copyright (c) Ladislav Zezula 2021 */ /*---------------------------------------------------------------------------*/ /* Don't call this module "Socket.cpp", otherwise VS 2019 will not link it */ /* Socket functions for CascLib. */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 13.02.21 1.00 Lad Created */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" //----------------------------------------------------------------------------- // Local variables CASC_SOCKET_CACHE SocketCache; //----------------------------------------------------------------------------- // CASC_SOCKET functions // Guarantees that there is zero terminator after the response char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, size_t * PtrLength) { CASC_MIME_HTTP HttpInfo; char * server_response = NULL; size_t total_received = 0; size_t block_increment = 0x8000; size_t buffer_size = block_increment; int bytes_received = 0; // Pre-set the result length if(PtrLength != NULL) PtrLength[0] = 0; if(request_length == 0) request_length = strlen(request); // Lock the socket CascLock(Lock); // Send the request to the remote host. On Linux, this call may send signal(SIGPIPE), // we need to prevend that by using the MSG_NOSIGNAL flag. On Windows, it fails normally. while(send(sock, request, (int)request_length, MSG_NOSIGNAL) == SOCKET_ERROR) { // If the connection was closed by the remote host, we try to reconnect if(ReconnectAfterShutdown(sock, remoteItem) == INVALID_SOCKET) { SetCascError(ERROR_NETWORK_NOT_AVAILABLE); CascUnlock(Lock); return NULL; } } // Allocate buffer for server response. Allocate one extra byte for zero terminator if((server_response = CASC_ALLOC(buffer_size + 1)) != NULL) { for(;;) { // Reallocate the buffer size, if needed if(total_received == buffer_size) { if((server_response = CASC_REALLOC(char, server_response, buffer_size + block_increment + 1)) == NULL) { SetCascError(ERROR_NOT_ENOUGH_MEMORY); CascUnlock(Lock); return NULL; } buffer_size += block_increment; } // Receive the next part of the response, up to buffer size // Return value 0 means "connection closed", -1 means an error bytes_received = recv(sock, server_response + total_received, (int)(buffer_size - total_received), 0); if(bytes_received <= 0) break; // Append the number of bytes received. Also terminate response with zero total_received += bytes_received; server_response[total_received] = 0; // On a HTTP protocol, we need to check whether we received all data if(HttpInfo.IsDataComplete(server_response, total_received)) break; } } // Unlock the socket CascUnlock(Lock); // Give the result to the caller if(PtrLength != NULL) PtrLength[0] = total_received; return server_response; } DWORD CASC_SOCKET::AddRef() { return CascInterlockedIncrement(&dwRefCount); } void CASC_SOCKET::Release() { // Note: If this is a cached socket, there will be extra reference from the cache if(CascInterlockedDecrement(&dwRefCount) == 0) { Delete(); } } int CASC_SOCKET::GetSockError() { #ifdef CASCLIB_PLATFORM_WINDOWS return WSAGetLastError(); #else return errno; #endif } DWORD CASC_SOCKET::GetAddrInfoWrapper(const char * hostName, unsigned portNum, PADDRINFO hints, PADDRINFO * ppResult) { char portNumString[16]; // Prepare the port number CascStrPrintf(portNumString, _countof(portNumString), "%d", portNum); // Attempt to connect for(;;) { // Attempt to call the addrinfo DWORD dwErrCode = getaddrinfo(hostName, portNumString, hints, ppResult); // Error-specific handling switch(dwErrCode) { #ifdef CASCLIB_PLATFORM_WINDOWS case WSANOTINITIALISED: // Windows-specific: WSAStartup not called { WSADATA wsd; WSAStartup(MAKEWORD(2, 2), &wsd); continue; } #endif case (DWORD)EAI_AGAIN: // Temporary error, try again continue; default: // Any other result, incl. ERROR_SUCCESS return dwErrCode; } } } SOCKET CASC_SOCKET::CreateAndConnect(addrinfo * remoteItem) { SOCKET sock; // Create new socket // On error, returns returns INVALID_SOCKET (-1) on Windows, -1 on Linux if((sock = socket(remoteItem->ai_family, remoteItem->ai_socktype, remoteItem->ai_protocol)) > 0) { // Connect to the remote host // On error, returns SOCKET_ERROR (-1) on Windows, -1 on Linux if(connect(sock, remoteItem->ai_addr, (int)remoteItem->ai_addrlen) == 0) return sock; // Failed. Close the socket and return 0 closesocket(sock); sock = INVALID_SOCKET; } return sock; } SOCKET CASC_SOCKET::ReconnectAfterShutdown(SOCKET & sock, addrinfo * remoteItem) { // Retrieve the error code related to previous socket operation switch(GetSockError()) { case EPIPE: // Non-Windows case WSAECONNRESET: // Windows { // Close the old socket if(sock != INVALID_SOCKET) closesocket(sock); // Attempt to reconnect sock = CreateAndConnect(remoteItem); return sock; } } // Another problem return INVALID_SOCKET; } PCASC_SOCKET CASC_SOCKET::New(addrinfo * remoteList, addrinfo * remoteItem, const char * hostName, unsigned portNum, SOCKET sock) { PCASC_SOCKET pSocket; size_t length = strlen(hostName); // Allocate enough bytes pSocket = (PCASC_SOCKET)CASC_ALLOC(sizeof(CASC_SOCKET) + length); if(pSocket != NULL) { // Fill the entire object with zero memset(pSocket, 0, sizeof(CASC_SOCKET) + length); pSocket->remoteList = remoteList; pSocket->remoteItem = remoteItem; pSocket->dwRefCount = 1; pSocket->portNum = portNum; pSocket->sock = sock; // Init the remote host name CascStrCopy((char *)pSocket->hostName, length + 1, hostName); // Init the socket lock CascInitLock(pSocket->Lock); } return pSocket; } PCASC_SOCKET CASC_SOCKET::Connect(const char * hostName, unsigned portNum) { PCASC_SOCKET pSocket; addrinfo * remoteList; addrinfo * remoteItem; addrinfo hints = {0}; SOCKET sock; int nErrCode; // Retrieve the information about the remote host // This will fail immediately if there is no connection to the internet hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; nErrCode = GetAddrInfoWrapper(hostName, portNum, &hints, &remoteList); // Handle error code if(nErrCode == 0) { // Try to connect to any address provided by the getaddrinfo() for(remoteItem = remoteList; remoteItem != NULL; remoteItem = remoteItem->ai_next) { // Create new socket and connect to the remote host if((sock = CreateAndConnect(remoteItem)) != 0) { // Create new instance of the CASC_SOCKET structure if((pSocket = CASC_SOCKET::New(remoteList, remoteItem, hostName, portNum, sock)) != NULL) { return pSocket; } // Close the socket closesocket(sock); } } // Couldn't find a network nErrCode = ERROR_NETWORK_NOT_AVAILABLE; } SetCascError(nErrCode); return NULL; } void CASC_SOCKET::Delete() { PCASC_SOCKET pThis = this; // Remove the socket from the cache if(pCache != NULL) pCache->UnlinkSocket(this); pCache = NULL; // Close the socket, if any if(sock != 0) closesocket(sock); sock = 0; // Free the lock CascFreeLock(Lock); // Free the socket itself CASC_FREE(pThis); } //----------------------------------------------------------------------------- // The CASC_SOCKET_CACHE class CASC_SOCKET_CACHE::CASC_SOCKET_CACHE() { pFirst = pLast = NULL; dwRefCount = 0; } CASC_SOCKET_CACHE::~CASC_SOCKET_CACHE() { PurgeAll(); } PCASC_SOCKET CASC_SOCKET_CACHE::Find(const char * hostName, unsigned portNum) { PCASC_SOCKET pSocket; for(pSocket = pFirst; pSocket != NULL; pSocket = pSocket->pNext) { if(!_stricmp(pSocket->hostName, hostName) && (pSocket->portNum == portNum)) break; } return pSocket; } PCASC_SOCKET CASC_SOCKET_CACHE::InsertSocket(PCASC_SOCKET pSocket) { if(pSocket != NULL && pSocket->pCache == NULL) { // Do we have caching turned on? if(dwRefCount > 0) { // Insert one reference to the socket to mark it as cached pSocket->AddRef(); // Insert the socket to the chain if(pFirst == NULL && pLast == NULL) { pFirst = pLast = pSocket; } else { pSocket->pPrev = pLast; pLast->pNext = pSocket; pLast = pSocket; } // Mark the socket as cached pSocket->pCache = this; } } return pSocket; } void CASC_SOCKET_CACHE::UnlinkSocket(PCASC_SOCKET pSocket) { // Only if it's a valid socket if(pSocket != NULL) { // Check the first and the last items if(pSocket == pFirst) pFirst = pSocket->pNext; if(pSocket == pLast) pLast = pSocket->pPrev; // Disconnect the socket from the chain if(pSocket->pPrev != NULL) pSocket->pPrev->pNext = pSocket->pNext; if(pSocket->pNext != NULL) pSocket->pNext->pPrev = pSocket->pPrev; } } void CASC_SOCKET_CACHE::SetCaching(bool bAddRef) { PCASC_SOCKET pSocket; PCASC_SOCKET pNext; // We need to increment reference count for each enabled caching if(bAddRef) { // Add one reference to all currently held sockets if(dwRefCount == 0) { for(pSocket = pFirst; pSocket != NULL; pSocket = pSocket->pNext) pSocket->AddRef(); } // Increment of references for the future sockets CascInterlockedIncrement(&dwRefCount); } else { // Sanity check for multiple calls to dereference assert(dwRefCount > 0); // Dereference the reference count. If drops to zero, dereference all sockets as well if(CascInterlockedDecrement(&dwRefCount) == 0) { for(pSocket = pFirst; pSocket != NULL; pSocket = pNext) { pNext = pSocket->pNext; pSocket->Release(); } } } } void CASC_SOCKET_CACHE::PurgeAll() { PCASC_SOCKET pSocket; PCASC_SOCKET pNext; // Dereference all current sockets for(pSocket = pFirst; pSocket != NULL; pSocket = pNext) { pNext = pSocket->pNext; pSocket->Delete(); } } //----------------------------------------------------------------------------- // Public functions PCASC_SOCKET sockets_connect(const char * hostName, unsigned portNum) { PCASC_SOCKET pSocket; // Try to find the item in the cache if((pSocket = SocketCache.Find(hostName, portNum)) != NULL) { pSocket->AddRef(); } else { // Create new socket and connect it to the remote host pSocket = CASC_SOCKET::Connect(hostName, portNum); // Insert it to the cache, if it's a HTTP connection if(pSocket->portNum == CASC_PORT_HTTP) pSocket = SocketCache.InsertSocket(pSocket); } return pSocket; } void sockets_set_caching(bool caching) { SocketCache.SetCaching(caching); } ================================================ FILE: deps/CascLib/src/common/Sockets.h ================================================ /*****************************************************************************/ /* Sockets.h Copyright (c) Ladislav Zezula 2021 */ /*---------------------------------------------------------------------------*/ /* MIME parsing functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 13.02.21 1.00 Lad Created */ /*****************************************************************************/ #ifndef __SOCKET_H__ #define __SOCKET_H__ //----------------------------------------------------------------------------- // Defines #ifndef INVALID_SOCKET #define INVALID_SOCKET (SOCKET)(-1) #endif #ifndef SOCKET_ERROR #define SOCKET_ERROR (-1) #endif #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #ifndef WSAECONNRESET #define WSAECONNRESET 10054L #endif #ifndef EPIPE #define EPIPE 32 #endif #define CASC_PORT_HTTP 80 #define CASC_PORT_RIBBIT 1119 //----------------------------------------------------------------------------- // The CASC_SOCKET class typedef class CASC_SOCKET_CACHE * PCASC_SOCKET_CACHE; typedef class CASC_SOCKET * PCASC_SOCKET; typedef struct addrinfo * PADDRINFO; class CASC_SOCKET { public: char * ReadResponse(const char * request, size_t request_length = 0, size_t * PtrLength = NULL); DWORD AddRef(); void Release(); private: // Constructor and destructor static int GetSockError(); static DWORD GetAddrInfoWrapper(const char * hostName, unsigned portNum, PADDRINFO hints, PADDRINFO * ppResult); static SOCKET CreateAndConnect(addrinfo * remoteItem); static SOCKET ReconnectAfterShutdown(SOCKET & sock, addrinfo * remoteItem); static PCASC_SOCKET New(addrinfo * remoteList, addrinfo * remoteItem, const char * hostName, unsigned portNum, SOCKET sock); static PCASC_SOCKET Connect(const char * hostName, unsigned portNum); // Frees all resources and deletes the socket void Delete(); // Entities allowed to manipulate with the class friend CASC_SOCKET * sockets_connect(const char * hostName, unsigned portNum); friend char * sockets_read_response(PCASC_SOCKET pSocket, const char * request, size_t request_length, size_t * PtrLength); friend class CASC_SOCKET_CACHE; PCASC_SOCKET_CACHE pCache; // Pointer to the cache. If NULL, the socket is not cached PCASC_SOCKET pPrev; // Pointer to the prev socket in the list PCASC_SOCKET pNext; // Pointer to the next socket in the list PADDRINFO remoteList; // List of the remote host informations PADDRINFO remoteItem; // The particular host picked during the last connection attempt CASC_LOCK Lock; // Lock for single threaded access SOCKET sock; // Opened and connected socket DWORD dwRefCount; // Number of references DWORD portNum; // Port number char hostName[1]; // Buffer for storing remote host (variable length) }; //----------------------------------------------------------------------------- // Socket cache class class CASC_SOCKET_CACHE { public: CASC_SOCKET_CACHE(); ~CASC_SOCKET_CACHE(); PCASC_SOCKET Find(const char * hostName, unsigned portNum); PCASC_SOCKET InsertSocket(PCASC_SOCKET pSocket); void UnlinkSocket(PCASC_SOCKET pSocket); void SetCaching(bool bAddRef); void PurgeAll(); private: PCASC_SOCKET pFirst; PCASC_SOCKET pLast; DWORD dwRefCount; }; //----------------------------------------------------------------------------- // Public functions PCASC_SOCKET sockets_connect(const char * hostName, unsigned portNum); void sockets_set_caching(bool caching); #endif // __SOCKET_H__ ================================================ FILE: deps/CascLib/src/jenkins/lookup.h ================================================ #ifndef __LOOKUP3_H__ #define __LOOKUP3_H__ #ifdef WIN32 typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else #include /* defines uint32_t etc */ #endif #ifdef __cplusplus extern "C" { #endif uint32_t hashlittle(const void *key, size_t length, uint32_t initval); void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); #ifdef __cplusplus } #endif #endif // __LOOKUP3_H__ ================================================ FILE: deps/CascLib/src/jenkins/lookup3.c ================================================ /* ------------------------------------------------------------------------------- lookup3.c, by Bob Jenkins, May 2006, Public Domain. These are functions for producing 32-bit hashes for hash table lookup. hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() are externally useful functions. Routines to test the hash are included if SELF_TEST is defined. You can use this free for any purpose. It's in the public domain. It has no warranty. You probably want to use hashlittle(). hashlittle() and hashbig() hash byte arrays. hashlittle() is is faster than hashbig() on little-endian machines. Intel and AMD are little-endian machines. On second thought, you probably want hashlittle2(), which is identical to hashlittle() except it returns two 32-bit hashes for the price of one. You could implement hashbig2() if you wanted but I haven't bothered here. If you want to find a hash of, say, exactly 7 integers, do a = i1; b = i2; c = i3; mix(a,b,c); a += i4; b += i5; c += i6; mix(a,b,c); a += i7; final(a,b,c); then use c as the hash value. If you have a variable length array of 4-byte integers to hash, use hashword(). If you have a byte array (like a character string), use hashlittle(). If you have several byte arrays, or a mix of things, see the comments above hashlittle(). Why is this so big? I read 12 bytes at a time into 3 4-byte integers, then mix those integers. This is fast (you can do a lot more thorough mixing with 12*3 instructions on 3 integers than you can with 3 instructions on 1 byte), but shoehorning those bytes into integers efficiently is messy. ------------------------------------------------------------------------------- */ //#define SELF_TEST 1 #include /* defines printf for tests */ #include /* defines time_t for timings in the test */ #ifdef linux #include /* attempt to define endianness */ #include /* attempt to define endianness */ #endif #include "lookup.h" /* * My best guess at if you are big-endian or little-endian. This may * need adjustment. */ #if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ __BYTE_ORDER == __LITTLE_ENDIAN) || \ (defined(i386) || defined(__i386__) || defined(__i486__) || \ defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) # define HASH_LITTLE_ENDIAN 1 # define HASH_BIG_ENDIAN 0 #elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ __BYTE_ORDER == __BIG_ENDIAN) || \ (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 1 #else # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 0 #endif #define hashsize(n) ((uint32_t)1<<(n)) #define hashmask(n) (hashsize(n)-1) #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) /* ------------------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. This is reversible, so any information in (a,b,c) before mix() is still in (a,b,c) after mix(). If four pairs of (a,b,c) inputs are run through mix(), or through mix() in reverse, there are at least 32 bits of the output that are sometimes the same for one pair and different for another pair. This was tested for: * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that satisfy this are 4 6 8 16 19 4 9 15 3 18 27 15 14 9 3 7 17 3 Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined as + with a one-bit base and a two-bit delta. I used http://burtleburtle.net/bob/hash/avalanche.html to choose the operations, constants, and arrangements of the variables. This does not achieve avalanche. There are input bits of (a,b,c) that fail to affect some output bits of (a,b,c), especially of a. The most thoroughly mixed value is c, but it doesn't really even achieve avalanche in c. This allows some parallelism. Read-after-writes are good at doubling the number of bits affected, so the goal of mixing pulls in the opposite direction as the goal of parallelism. I did what I could. Rotates seem to cost as much as shifts on every machine I could lay my hands on, and rotates are much kinder to the top and bottom bits, so I used rotates. ------------------------------------------------------------------------------- */ #define mix(a,b,c) \ { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } /* ------------------------------------------------------------------------------- final -- final mixing of 3 32-bit values (a,b,c) into c Pairs of (a,b,c) values differing in only a few bits will usually produce values of c that look totally different. This was tested for * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. These constants passed: 14 11 25 16 4 14 24 12 14 25 16 4 14 24 and these came close: 4 8 15 26 3 22 24 10 8 15 26 3 22 24 11 8 15 26 3 22 24 ------------------------------------------------------------------------------- */ #define final(a,b,c) \ { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c,4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } /* -------------------------------------------------------------------- This works on all machines. To be useful, it requires -- that the key be an array of uint32_t's, and -- that the length be the number of uint32_t's in the key The function hashword() is identical to hashlittle() on little-endian machines, and identical to hashbig() on big-endian machines, except that the length has to be measured in uint32_ts rather than in bytes. hashlittle() is more complicated than hashword() only because hashlittle() has to dance around fitting the key bytes into registers. -------------------------------------------------------------------- */ uint32_t hashword( const uint32_t *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32_t initval) /* the previous hash, or an arbitrary value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ return c; } /* -------------------------------------------------------------------- hashword2() -- same as hashword(), but take two seeds and return two 32-bit values. pc and pb must both be nonnull, and *pc and *pb must both be initialized with seeds. If you pass in (*pb)==0, the output (*pc) will be the same as the return value from hashword(). -------------------------------------------------------------------- */ void hashword2 ( const uint32_t *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32_t *pc, /* IN: seed OUT: primary hash value */ uint32_t *pb) /* IN: more seed OUT: secondary hash value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; c += *pb; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ *pc=c; *pb=b; } /* ------------------------------------------------------------------------------- hashlittle() -- hash a variable-length key into a 32-bit value k : the key (the unaligned variable-length array of bytes) length : the length of the key, counting by bytes initval : can be any 4-byte value Returns a 32-bit value. Every bit of the key affects every bit of the return value. Two keys differing by one or two bits will have totally different hash values. The best hash table sizes are powers of 2. There is no need to do mod a prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. For example, if you need only 10 bits, do h = (h & hashmask(10)); In which case, the hash table should have hashsize(10) elements. If you are hashing n strings (uint8_t **)k, do it like this: for (i=0, h=0; i 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]&0xffffff" actually reads beyond the end of the string, but * then masks off the part it's not allowed to read. Because the * string is aligned, the masked-off tail is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; case 0 : return c; /* zero length strings require no mixing */ } #else /* make valgrind happy */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ case 1 : a+=k8[0]; break; case 0 : return c; } #endif /* !valgrind */ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ const uint8_t *k8; /*--------------- all but last block: aligned reads and different mixing */ while (length > 12) { a += k[0] + (((uint32_t)k[1])<<16); b += k[2] + (((uint32_t)k[3])<<16); c += k[4] + (((uint32_t)k[5])<<16); mix(a,b,c); length -= 12; k += 6; } /*----------------------------- handle the last (probably partial) block */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[4]+(((uint32_t)k[5])<<16); b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; break; case 0 : return c; /* zero length requires no mixing */ } } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; a += ((uint32_t)k[1])<<8; a += ((uint32_t)k[2])<<16; a += ((uint32_t)k[3])<<24; b += k[4]; b += ((uint32_t)k[5])<<8; b += ((uint32_t)k[6])<<16; b += ((uint32_t)k[7])<<24; c += k[8]; c += ((uint32_t)k[9])<<8; c += ((uint32_t)k[10])<<16; c += ((uint32_t)k[11])<<24; mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; case 11: c+=((uint32_t)k[10])<<16; case 10: c+=((uint32_t)k[9])<<8; case 9 : c+=k[8]; case 8 : b+=((uint32_t)k[7])<<24; case 7 : b+=((uint32_t)k[6])<<16; case 6 : b+=((uint32_t)k[5])<<8; case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3])<<24; case 3 : a+=((uint32_t)k[2])<<16; case 2 : a+=((uint32_t)k[1])<<8; case 1 : a+=k[0]; break; case 0 : return c; } } final(a,b,c); return c; } /* * hashlittle2: return 2 32-bit hash values * * This is identical to hashlittle(), except it returns two 32-bit hash * values instead of just one. This is good enough for hash table * lookup with 2^^64 buckets, or if you want a second hash if you're not * happy with the first, or if you want a probably-unique 64-bit ID for * the key. *pc is better mixed than *pb, so use *pc first. If you want * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". */ void hashlittle2( const void *key, /* the key to hash */ size_t length, /* length of the key */ uint32_t *pc, /* IN: primary initval, OUT: primary hash */ uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ { uint32_t a,b,c; /* internal state */ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; c += *pb; u.ptr = key; if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ const uint8_t *k8; /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]&0xffffff" actually reads beyond the end of the string, but * then masks off the part it's not allowed to read. Because the * string is aligned, the masked-off tail is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } #else /* make valgrind happy */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ case 1 : a+=k8[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } #endif /* !valgrind */ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ const uint8_t *k8; /*--------------- all but last block: aligned reads and different mixing */ while (length > 12) { a += k[0] + (((uint32_t)k[1])<<16); b += k[2] + (((uint32_t)k[3])<<16); c += k[4] + (((uint32_t)k[5])<<16); mix(a,b,c); length -= 12; k += 6; } /*----------------------------- handle the last (probably partial) block */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[4]+(((uint32_t)k[5])<<16); b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; a += ((uint32_t)k[1])<<8; a += ((uint32_t)k[2])<<16; a += ((uint32_t)k[3])<<24; b += k[4]; b += ((uint32_t)k[5])<<8; b += ((uint32_t)k[6])<<16; b += ((uint32_t)k[7])<<24; c += k[8]; c += ((uint32_t)k[9])<<8; c += ((uint32_t)k[10])<<16; c += ((uint32_t)k[11])<<24; mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; case 11: c+=((uint32_t)k[10])<<16; case 10: c+=((uint32_t)k[9])<<8; case 9 : c+=k[8]; case 8 : b+=((uint32_t)k[7])<<24; case 7 : b+=((uint32_t)k[6])<<16; case 6 : b+=((uint32_t)k[5])<<8; case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3])<<24; case 3 : a+=((uint32_t)k[2])<<16; case 2 : a+=((uint32_t)k[1])<<8; case 1 : a+=k[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } } final(a,b,c); *pc=c; *pb=b; } /* * hashbig(): * This is the same as hashword() on big-endian machines. It is different * from hashlittle() on all machines. hashbig() takes advantage of * big-endian byte ordering. */ uint32_t hashbig( const void *key, size_t length, uint32_t initval) { uint32_t a,b,c; union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; u.ptr = key; if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ const uint8_t *k8; /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]<<8" actually reads beyond the end of the string, but * then shifts out the part it's not allowed to read. Because the * string is aligned, the illegal read is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; case 5 : b+=k[1]&0xff000000; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff00; break; case 2 : a+=k[0]&0xffff0000; break; case 1 : a+=k[0]&0xff000000; break; case 0 : return c; /* zero length strings require no mixing */ } #else /* make valgrind happy */ k8 = (const uint8_t *)k; switch(length) /* all the case statements fall through */ { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ case 1 : a+=((uint32_t)k8[0])<<24; break; case 0 : return c; } #endif /* !VALGRIND */ } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += ((uint32_t)k[0])<<24; a += ((uint32_t)k[1])<<16; a += ((uint32_t)k[2])<<8; a += ((uint32_t)k[3]); b += ((uint32_t)k[4])<<24; b += ((uint32_t)k[5])<<16; b += ((uint32_t)k[6])<<8; b += ((uint32_t)k[7]); c += ((uint32_t)k[8])<<24; c += ((uint32_t)k[9])<<16; c += ((uint32_t)k[10])<<8; c += ((uint32_t)k[11]); mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=k[11]; case 11: c+=((uint32_t)k[10])<<8; case 10: c+=((uint32_t)k[9])<<16; case 9 : c+=((uint32_t)k[8])<<24; case 8 : b+=k[7]; case 7 : b+=((uint32_t)k[6])<<8; case 6 : b+=((uint32_t)k[5])<<16; case 5 : b+=((uint32_t)k[4])<<24; case 4 : a+=k[3]; case 3 : a+=((uint32_t)k[2])<<8; case 2 : a+=((uint32_t)k[1])<<16; case 1 : a+=((uint32_t)k[0])<<24; break; case 0 : return c; } } final(a,b,c); return c; } #ifdef SELF_TEST /* used for timings */ void driver1() { uint8_t buf[256]; uint32_t i; uint32_t h=0; time_t a,z; time(&a); for (i=0; i<256; ++i) buf[i] = 'x'; for (i=0; i<1; ++i) { h = hashlittle(&buf[0],1,h); } time(&z); if (z-a > 0) printf("time %d %.8x\n", z-a, h); } /* check that every input bit changes every output bit half the time */ #define HASHSTATE 1 #define HASHLEN 1 #define MAXPAIR 60 #define MAXLEN 70 void driver2() { uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; uint32_t x[HASHSTATE],y[HASHSTATE]; uint32_t hlen; printf("No more than %d trials should ever be needed \n",MAXPAIR/2); for (hlen=0; hlen < MAXLEN; ++hlen) { z=0; for (i=0; i>(8-j)); c[0] = hashlittle(a, hlen, m); b[i] ^= ((k+1)<>(8-j)); d[0] = hashlittle(b, hlen, m); /* check every bit is 1, 0, set, and not set at least once */ for (l=0; lz) z=k; if (k==MAXPAIR) { printf("Some bit didn't change: "); printf("%.8x %.8x %.8x %.8x %.8x %.8x ", e[0],f[0],g[0],h[0],x[0],y[0]); printf("i %d j %d m %d len %d\n", i, j, m, hlen); } if (z==MAXPAIR) goto done; } } } done: if (z < MAXPAIR) { printf("Mix success %2d bytes %2d initvals ",i,m); printf("required %d trials\n", z/2); } } printf("\n"); } /* Check for reading beyond the end of the buffer and alignment problems */ void driver3() { uint8_t buf[MAXLEN+20], *b; uint32_t len; uint8_t q[] = "This is the time for all good men to come to the aid of their country..."; uint32_t h; uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country..."; uint32_t i; uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; uint32_t j; uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; uint32_t ref,x,y; uint8_t *p; printf("Endianness. These lines should all be the same (for values filled in):\n"); printf("%.8x %.8x %.8x\n", hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13), hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13), hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13)); p = q; printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); p = &qq[1]; printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); p = &qqq[2]; printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); p = &qqqq[3]; printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); printf("\n"); /* check that hashlittle2 and hashlittle produce the same results */ i=47; j=0; hashlittle2(q, sizeof(q), &i, &j); if (hashlittle(q, sizeof(q), 47) != i) printf("hashlittle2 and hashlittle mismatch\n"); /* check that hashword2 and hashword produce the same results */ len = 0xdeadbeef; i=47, j=0; hashword2(&len, 1, &i, &j); if (hashword(&len, 1, 47) != i) printf("hashword2 and hashword mismatch %x %x\n", i, hashword(&len, 1, 47)); /* check hashlittle doesn't read before or after the ends of the string */ for (h=0, b=buf+1; h<8; ++h, ++b) { for (i=0; i * * This software was written by Alexander Peslyak in 2001. No copyright is * claimed, and the software is hereby placed in the public domain. * In case this attempt to disclaim copyright and place the software in the * public domain is deemed null and void, then the software is * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the * general public under the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. * * There's ABSOLUTELY NO WARRANTY, express or implied. * * (This is a heavily cut-down "BSD license".) * * This differs from Colin Plumb's older public domain implementation in that * no exactly 32-bit integer data type is required (any 32-bit or wider * unsigned integer data type will do), there's no compile-time endianness * configuration, and the function prototypes match OpenSSL's. No code from * Colin Plumb's implementation has been reused; this comment merely compares * the properties of the two independent implementations. * * The primary goals of this implementation are portability and ease of use. * It is meant to be fast, but not as fast as possible. Some known * optimizations are not included to reduce source code size and avoid * compile-time configuration. */ #ifndef HAVE_OPENSSL #include #include "md5.h" /* * The basic MD5 functions. * * F and G are optimized compared to their RFC 1321 definitions for * architectures that lack an AND-NOT instruction, just like in Colin Plumb's * implementation. */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) #define H(x, y, z) (((x) ^ (y)) ^ (z)) #define H2(x, y, z) ((x) ^ ((y) ^ (z))) #define I(x, y, z) ((y) ^ ((x) | ~(z))) /* * The MD5 transformation for all four rounds. */ #define STEP(f, a, b, c, d, x, t, s) \ (a) += f((b), (c), (d)) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b); /* * SET reads 4 input bytes in little-endian byte order and stores them in a * properly aligned word in host byte order. * * The check for little-endian architectures that tolerate unaligned memory * accesses is just an optimization. Nothing will break if it fails to detect * a suitable architecture. * * Unfortunately, this optimization may be a C strict aliasing rules violation * if the caller's data buffer has effective type that cannot be aliased by * MD5_u32plus. In practice, this problem may occur if these MD5 routines are * inlined into a calling function, or with future and dangerously advanced * link-time optimizations. For the time being, keeping these MD5 routines in * their own translation unit avoids the problem. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) #define SET(n) \ (*(MD5_u32plus *)&ptr[(n) * 4]) #define GET(n) \ SET(n) #else #define SET(n) \ (ctx->block[(n)] = \ (MD5_u32plus)ptr[(n) * 4] | \ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) #define GET(n) \ (ctx->block[(n)]) #endif /* * This processes one or more 64-byte data blocks, but does NOT update the bit * counters. There are no alignment requirements. */ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) { const unsigned char *ptr; MD5_u32plus a, b, c, d; MD5_u32plus saved_a, saved_b, saved_c, saved_d; ptr = (const unsigned char *)data; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; do { saved_a = a; saved_b = b; saved_c = c; saved_d = d; /* Round 1 */ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) STEP(F, c, d, a, b, SET(2), 0x242070db, 17) STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) /* Round 2 */ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) STEP(G, d, a, b, c, GET(10), 0x02441453, 9) STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) /* Round 3 */ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) /* Round 4 */ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) a += saved_a; b += saved_b; c += saved_c; d += saved_d; ptr += 64; } while (size -= 64); ctx->a = a; ctx->b = b; ctx->c = c; ctx->d = d; return ptr; } void MD5_Init(MD5_CTX *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->lo = 0; ctx->hi = 0; } void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) { MD5_u32plus saved_lo; unsigned long used, available; saved_lo = ctx->lo; if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) ctx->hi++; ctx->hi += size >> 29; used = saved_lo & 0x3f; if (used) { available = 64 - used; if (size < available) { memcpy(&ctx->buffer[used], data, size); return; } memcpy(&ctx->buffer[used], data, available); data = (const unsigned char *)data + available; size -= available; body(ctx, ctx->buffer, 64); } if (size >= 64) { data = body(ctx, data, size & ~(unsigned long)0x3f); size &= 0x3f; } memcpy(ctx->buffer, data, size); } #define PUT_VALUE32(dst, src) \ (dst)[0] = (unsigned char)(src); \ (dst)[1] = (unsigned char)((src) >> 8); \ (dst)[2] = (unsigned char)((src) >> 16); \ (dst)[3] = (unsigned char)((src) >> 24); void MD5_Final(unsigned char *result, MD5_CTX *ctx) { unsigned long used, available; used = ctx->lo & 0x3f; ctx->buffer[used++] = 0x80; available = 64 - used; if (available < 8) { memset(&ctx->buffer[used], 0, available); body(ctx, ctx->buffer, 64); used = 0; available = 64; } memset(&ctx->buffer[used], 0, available - 8); ctx->lo <<= 3; PUT_VALUE32(&ctx->buffer[56], ctx->lo) PUT_VALUE32(&ctx->buffer[60], ctx->hi) body(ctx, ctx->buffer, 64); PUT_VALUE32(&result[0], ctx->a) PUT_VALUE32(&result[4], ctx->b) PUT_VALUE32(&result[8], ctx->c) PUT_VALUE32(&result[12], ctx->d) memset(ctx, 0, sizeof(*ctx)); } #endif ================================================ FILE: deps/CascLib/src/md5/md5.h ================================================ /* * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. * MD5 Message-Digest Algorithm (RFC 1321). * * Homepage: * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 * * Author: * Alexander Peslyak, better known as Solar Designer * * This software was written by Alexander Peslyak in 2001. No copyright is * claimed, and the software is hereby placed in the public domain. * In case this attempt to disclaim copyright and place the software in the * public domain is deemed null and void, then the software is * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the * general public under the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. * * There's ABSOLUTELY NO WARRANTY, express or implied. * * See md5.c for more information. */ #ifdef HAVE_OPENSSL #include #elif !defined(_MD5_H) #define _MD5_H /* Any 32-bit or wider unsigned integer data type will do */ typedef unsigned int MD5_u32plus; typedef struct { MD5_u32plus lo, hi; MD5_u32plus a, b, c, d; unsigned char buffer[64]; MD5_u32plus block[16]; } MD5_CTX; void MD5_Init(MD5_CTX *ctx); void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); void MD5_Final(unsigned char *result, MD5_CTX *ctx); #endif ================================================ FILE: deps/CascLib/src/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by DllMain.rc // // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: deps/CascLib/src/zlib/adler32.c ================================================ /* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995-2011, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); #define BASE 65521U /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /* use NO_DIVIDE if your processor does not do division in hardware -- try it both ways to see which is faster */ #ifdef NO_DIVIDE /* note that this assumes BASE is 65521, where 65536 % 65521 == 15 (thank you to John Reiser for pointing this out) */ # define CHOP(a) \ do { \ unsigned long tmp = a >> 16; \ a &= 0xffffUL; \ a += (tmp << 4) - tmp; \ } while (0) # define MOD28(a) \ do { \ CHOP(a); \ if (a >= BASE) a -= BASE; \ } while (0) # define MOD(a) \ do { \ CHOP(a); \ MOD28(a); \ } while (0) # define MOD63(a) \ do { /* this assumes a is not negative */ \ z_off64_t tmp = a >> 32; \ a &= 0xffffffffL; \ a += (tmp << 8) - (tmp << 5) + tmp; \ tmp = a >> 16; \ a &= 0xffffL; \ a += (tmp << 4) - tmp; \ tmp = a >> 16; \ a &= 0xffffL; \ a += (tmp << 4) - tmp; \ if (a >= BASE) a -= BASE; \ } while (0) #else # define MOD(a) a %= BASE # define MOD28(a) a %= BASE # define MOD63(a) a %= BASE #endif /* ========================================================================= */ uLong ZEXPORT adler32_z(adler, buf, len) uLong adler; const Bytef *buf; z_size_t len; { unsigned long sum2; unsigned n; /* split Adler-32 into component sums */ sum2 = (adler >> 16) & 0xffff; adler &= 0xffff; /* in case user likes doing a byte at a time, keep it fast */ if (len == 1) { adler += buf[0]; if (adler >= BASE) adler -= BASE; sum2 += adler; if (sum2 >= BASE) sum2 -= BASE; return adler | (sum2 << 16); } /* initial Adler-32 value (deferred check for len == 1 speed) */ if (buf == Z_NULL) return 1L; /* in case short lengths are provided, keep it somewhat fast */ if (len < 16) { while (len--) { adler += *buf++; sum2 += adler; } if (adler >= BASE) adler -= BASE; MOD28(sum2); /* only added so many BASE's */ return adler | (sum2 << 16); } /* do length NMAX blocks -- requires just one modulo operation */ while (len >= NMAX) { len -= NMAX; n = NMAX / 16; /* NMAX is divisible by 16 */ do { DO16(buf); /* 16 sums unrolled */ buf += 16; } while (--n); MOD(adler); MOD(sum2); } /* do remaining bytes (less than NMAX, still just one modulo) */ if (len) { /* avoid modulos if none remaining */ while (len >= 16) { len -= 16; DO16(buf); buf += 16; } while (len--) { adler += *buf++; sum2 += adler; } MOD(adler); MOD(sum2); } /* return recombined sums */ return adler | (sum2 << 16); } /* ========================================================================= */ uLong ZEXPORT adler32(adler, buf, len) uLong adler; const Bytef *buf; uInt len; { return adler32_z(adler, buf, len); } /* ========================================================================= */ local uLong adler32_combine_(adler1, adler2, len2) uLong adler1; uLong adler2; z_off64_t len2; { unsigned long sum1; unsigned long sum2; unsigned rem; /* for negative len, return invalid adler32 as a clue for debugging */ if (len2 < 0) return 0xffffffffUL; /* the derivation of this formula is left as an exercise for the reader */ MOD63(len2); /* assumes len2 >= 0 */ rem = (unsigned)len2; sum1 = adler1 & 0xffff; sum2 = rem * sum1; MOD(sum2); sum1 += (adler2 & 0xffff) + BASE - 1; sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; if (sum1 >= BASE) sum1 -= BASE; if (sum1 >= BASE) sum1 -= BASE; if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1); if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } /* ========================================================================= */ uLong ZEXPORT adler32_combine(adler1, adler2, len2) uLong adler1; uLong adler2; z_off_t len2; { return adler32_combine_(adler1, adler2, len2); } uLong ZEXPORT adler32_combine64(adler1, adler2, len2) uLong adler1; uLong adler2; z_off64_t len2; { return adler32_combine_(adler1, adler2, len2); } ================================================ FILE: deps/CascLib/src/zlib/crc32.c ================================================ /* crc32.c -- compute the CRC-32 of a data stream * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Thanks to Rodney Brown for his contribution of faster * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing * tables for updating the shift register in one step with three exclusive-ors * instead of four steps with four exclusive-ors. This results in about a * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. */ /* @(#) $Id$ */ /* Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore protection on the static variables used to control the first-use generation of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should first call get_crc_table() to initialize the tables before allowing more than one thread to use crc32(). DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. */ #ifdef MAKECRCH # include # ifndef DYNAMIC_CRC_TABLE # define DYNAMIC_CRC_TABLE # endif /* !DYNAMIC_CRC_TABLE */ #endif /* MAKECRCH */ #include "zutil.h" /* for STDC and FAR definitions */ /* Definitions for doing the crc four data bytes at a time. */ #if !defined(NOBYFOUR) && defined(Z_U4) # define BYFOUR #endif #ifdef BYFOUR local unsigned long crc32_little OF((unsigned long, const unsigned char FAR *, z_size_t)); local unsigned long crc32_big OF((unsigned long, const unsigned char FAR *, z_size_t)); # define TBLS 8 #else # define TBLS 1 #endif /* BYFOUR */ /* Local functions for crc concatenation */ local unsigned long gf2_matrix_times OF((unsigned long *mat, unsigned long vec)); local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); #ifdef DYNAMIC_CRC_TABLE local volatile int crc_table_empty = 1; local z_crc_t FAR crc_table[TBLS][256]; local void make_crc_table OF((void)); #ifdef MAKECRCH local void write_table OF((FILE *, const z_crc_t FAR *)); #endif /* MAKECRCH */ /* Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. Polynomials over GF(2) are represented in binary, one bit per coefficient, with the lowest powers in the most significant bit. Then adding polynomials is just exclusive-or, and multiplying a polynomial by x is a right shift by one. If we call the above polynomial p, and represent a byte as the polynomial q, also with the lowest power in the most significant bit (so the byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, where a mod b means the remainder after dividing a by b. This calculation is done using the shift-register method of multiplying and taking the remainder. The register is initialized to zero, and for each incoming bit, x^32 is added mod p to the register if the bit is a one (where x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by x (which is shifting right by one and adding x^32 mod p if the bit shifted out is a one). We start with the highest power (least significant bit) of q and repeat for all eight bits of q. The first table is simply the CRC of all possible eight bit values. This is all the information needed to generate CRCs on data a byte at a time for all combinations of CRC register values and incoming bytes. The remaining tables allow for word-at-a-time CRC calculation for both big-endian and little- endian machines, where a word is four bytes. */ local void make_crc_table() { z_crc_t c; int n, k; z_crc_t poly; /* polynomial exclusive-or pattern */ /* terms of polynomial defining this crc (except x^32): */ static volatile int first = 1; /* flag to limit concurrent making */ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; /* See if another task is already doing this (not thread-safe, but better than nothing -- significantly reduces duration of vulnerability in case the advice about DYNAMIC_CRC_TABLE is ignored) */ if (first) { first = 0; /* make exclusive-or pattern from polynomial (0xedb88320UL) */ poly = 0; for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) poly |= (z_crc_t)1 << (31 - p[n]); /* generate a crc for every 8-bit value */ for (n = 0; n < 256; n++) { c = (z_crc_t)n; for (k = 0; k < 8; k++) c = c & 1 ? poly ^ (c >> 1) : c >> 1; crc_table[0][n] = c; } #ifdef BYFOUR /* generate crc for each value followed by one, two, and three zeros, and then the byte reversal of those as well as the first table */ for (n = 0; n < 256; n++) { c = crc_table[0][n]; crc_table[4][n] = ZSWAP32(c); for (k = 1; k < 4; k++) { c = crc_table[0][c & 0xff] ^ (c >> 8); crc_table[k][n] = c; crc_table[k + 4][n] = ZSWAP32(c); } } #endif /* BYFOUR */ crc_table_empty = 0; } else { /* not first */ /* wait for the other guy to finish (not efficient, but rare) */ while (crc_table_empty) ; } #ifdef MAKECRCH /* write out CRC tables to crc32.h */ { FILE *out; out = fopen("crc32.h", "w"); if (out == NULL) return; fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); fprintf(out, "local const z_crc_t FAR "); fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); write_table(out, crc_table[0]); # ifdef BYFOUR fprintf(out, "#ifdef BYFOUR\n"); for (k = 1; k < 8; k++) { fprintf(out, " },\n {\n"); write_table(out, crc_table[k]); } fprintf(out, "#endif\n"); # endif /* BYFOUR */ fprintf(out, " }\n};\n"); fclose(out); } #endif /* MAKECRCH */ } #ifdef MAKECRCH local void write_table(out, table) FILE *out; const z_crc_t FAR *table; { int n; for (n = 0; n < 256; n++) fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", (unsigned long)(table[n]), n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); } #endif /* MAKECRCH */ #else /* !DYNAMIC_CRC_TABLE */ /* ======================================================================== * Tables of CRC-32s of all single-byte values, made by make_crc_table(). */ #include "crc32.h" #endif /* DYNAMIC_CRC_TABLE */ /* ========================================================================= * This function can be used by asm versions of crc32() */ const z_crc_t FAR * ZEXPORT get_crc_table() { #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ return (const z_crc_t FAR *)crc_table; } /* ========================================================================= */ #define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) #define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 /* ========================================================================= */ unsigned long ZEXPORT crc32_z(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; z_size_t len; { if (buf == Z_NULL) return 0UL; #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ #ifdef BYFOUR if (sizeof(void *) == sizeof(ptrdiff_t)) { z_crc_t endian; endian = 1; if (*((unsigned char *)(&endian))) return crc32_little(crc, buf, len); else return crc32_big(crc, buf, len); } #endif /* BYFOUR */ crc = crc ^ 0xffffffffUL; while (len >= 8) { DO8; len -= 8; } if (len) do { DO1; } while (--len); return crc ^ 0xffffffffUL; } /* ========================================================================= */ unsigned long ZEXPORT crc32(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; uInt len; { return crc32_z(crc, buf, len); } #ifdef BYFOUR /* This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit integer pointer type. This violates the strict aliasing rule, where a compiler can assume, for optimization purposes, that two pointers to fundamentally different types won't ever point to the same memory. This can manifest as a problem only if one of the pointers is written to. This code only reads from those pointers. So long as this code remains isolated in this compilation unit, there won't be a problem. For this reason, this code should not be copied and pasted into a compilation unit in which other code writes to the buffer that is passed to these routines. */ /* ========================================================================= */ #define DOLIT4 c ^= *buf4++; \ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] #define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 /* ========================================================================= */ local unsigned long crc32_little(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; z_size_t len; { register z_crc_t c; register const z_crc_t FAR *buf4; c = (z_crc_t)crc; c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } buf4 = (const z_crc_t FAR *)(const void FAR *)buf; while (len >= 32) { DOLIT32; len -= 32; } while (len >= 4) { DOLIT4; len -= 4; } buf = (const unsigned char FAR *)buf4; if (len) do { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); } while (--len); c = ~c; return (unsigned long)c; } /* ========================================================================= */ #define DOBIG4 c ^= *buf4++; \ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] #define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 /* ========================================================================= */ local unsigned long crc32_big(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; z_size_t len; { register z_crc_t c; register const z_crc_t FAR *buf4; c = ZSWAP32((z_crc_t)crc); c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); len--; } buf4 = (const z_crc_t FAR *)(const void FAR *)buf; while (len >= 32) { DOBIG32; len -= 32; } while (len >= 4) { DOBIG4; len -= 4; } buf = (const unsigned char FAR *)buf4; if (len) do { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); } while (--len); c = ~c; return (unsigned long)(ZSWAP32(c)); } #endif /* BYFOUR */ #define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ /* ========================================================================= */ local unsigned long gf2_matrix_times(mat, vec) unsigned long *mat; unsigned long vec; { unsigned long sum; sum = 0; while (vec) { if (vec & 1) sum ^= *mat; vec >>= 1; mat++; } return sum; } /* ========================================================================= */ local void gf2_matrix_square(square, mat) unsigned long *square; unsigned long *mat; { int n; for (n = 0; n < GF2_DIM; n++) square[n] = gf2_matrix_times(mat, mat[n]); } /* ========================================================================= */ local uLong crc32_combine_(crc1, crc2, len2) uLong crc1; uLong crc2; z_off64_t len2; { int n; unsigned long row; unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ /* degenerate case (also disallow negative lengths) */ if (len2 <= 0) return crc1; /* put operator for one zero bit in odd */ odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ row = 1; for (n = 1; n < GF2_DIM; n++) { odd[n] = row; row <<= 1; } /* put operator for two zero bits in even */ gf2_matrix_square(even, odd); /* put operator for four zero bits in odd */ gf2_matrix_square(odd, even); /* apply len2 zeros to crc1 (first square will put the operator for one zero byte, eight zero bits, in even) */ do { /* apply zeros operator for this bit of len2 */ gf2_matrix_square(even, odd); if (len2 & 1) crc1 = gf2_matrix_times(even, crc1); len2 >>= 1; /* if no more bits set, then done */ if (len2 == 0) break; /* another iteration of the loop with odd and even swapped */ gf2_matrix_square(odd, even); if (len2 & 1) crc1 = gf2_matrix_times(odd, crc1); len2 >>= 1; /* if no more bits set, then done */ } while (len2 != 0); /* return combined crc */ crc1 ^= crc2; return crc1; } /* ========================================================================= */ uLong ZEXPORT crc32_combine(crc1, crc2, len2) uLong crc1; uLong crc2; z_off_t len2; { return crc32_combine_(crc1, crc2, len2); } uLong ZEXPORT crc32_combine64(crc1, crc2, len2) uLong crc1; uLong crc2; z_off64_t len2; { return crc32_combine_(crc1, crc2, len2); } ================================================ FILE: deps/CascLib/src/zlib/crc32.h ================================================ /* crc32.h -- tables for rapid CRC calculation * Generated automatically by crc32.c */ local const z_crc_t FAR crc_table[TBLS][256] = { { 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, 0x2d02ef8dUL #ifdef BYFOUR }, { 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, 0x9324fd72UL }, { 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, 0xbe9834edUL }, { 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, 0xde0506f1UL }, { 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, 0x8def022dUL }, { 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, 0x72fd2493UL }, { 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, 0xed3498beUL }, { 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, 0xf10605deUL #endif } }; ================================================ FILE: deps/CascLib/src/zlib/deflate.c ================================================ /* deflate.c -- compress data using the deflation algorithm * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process depends on being able to identify portions * of the input text which are identical to earlier input (within a * sliding window trailing behind the input currently being processed). * * The most straightforward technique turns out to be the fastest for * most input files: try all possible matches and select the longest. * The key feature of this algorithm is that insertions into the string * dictionary are very simple and thus fast, and deletions are avoided * completely. Insertions are performed at each input character, whereas * string matches are performed only when the previous match ends. So it * is preferable to spend more time in matches to allow very fast string * insertions and avoid deletions. The matching algorithm for small * strings is inspired from that of Rabin & Karp. A brute force approach * is used to find longer strings when a small match has been found. * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze * (by Leonid Broukhis). * A previous version of this file used a more sophisticated algorithm * (by Fiala and Greene) which is guaranteed to run in linear amortized * time, but has a larger average cost, uses more memory and is patented. * However the F&G algorithm may be faster for some highly redundant * files if the parameter max_chain_length (described below) is too large. * * ACKNOWLEDGEMENTS * * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and * I found it in 'freeze' written by Leonid Broukhis. * Thanks to many people for bug reports and testing. * * REFERENCES * * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". * Available in http://tools.ietf.org/html/rfc1951 * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. * * Fiala,E.R., and Greene,D.H. * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 * */ /* @(#) $Id$ */ #include "deflate.h" const char deflate_copyright[] = " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* =========================================================================== * Function prototypes. */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ finish_started, /* finish started, need only more output at next deflate */ finish_done /* finish done, accept no more input or output */ } block_state; typedef block_state (*compress_func) OF((deflate_state *s, int flush)); /* Compression function. Returns the block state after the call. */ local int deflateStateCheck OF((z_streamp strm)); local void slide_hash OF((deflate_state *s)); local void fill_window OF((deflate_state *s)); local block_state deflate_stored OF((deflate_state *s, int flush)); local block_state deflate_fast OF((deflate_state *s, int flush)); #ifndef FASTEST local block_state deflate_slow OF((deflate_state *s, int flush)); #endif local block_state deflate_rle OF((deflate_state *s, int flush)); local block_state deflate_huff OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); #ifdef ASMV # pragma message("Assembler code may have bugs -- use at your own risk") void match_init OF((void)); /* asm code initialization */ uInt longest_match OF((deflate_state *s, IPos cur_match)); #else local uInt longest_match OF((deflate_state *s, IPos cur_match)); #endif #ifdef ZLIB_DEBUG local void check_match OF((deflate_state *s, IPos start, IPos match, int length)); #endif /* =========================================================================== * Local data */ #define NIL 0 /* Tail of hash chains */ #ifndef TOO_FAR # define TOO_FAR 4096 #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ typedef struct config_s { ush good_length; /* reduce lazy search above this match length */ ush max_lazy; /* do not perform lazy search above this match length */ ush nice_length; /* quit search above this match length */ ush max_chain; compress_func func; } config; #ifdef FASTEST local const config configuration_table[2] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ #else local const config configuration_table[10] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ /* 2 */ {4, 5, 16, 8, deflate_fast}, /* 3 */ {4, 6, 32, 32, deflate_fast}, /* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ /* 5 */ {8, 16, 32, 32, deflate_slow}, /* 6 */ {8, 16, 128, 128, deflate_slow}, /* 7 */ {8, 32, 128, 256, deflate_slow}, /* 8 */ {32, 128, 258, 1024, deflate_slow}, /* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ #endif /* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different * meaning. */ /* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ #define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0)) /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to UPDATE_HASH are made with consecutive input * characters, so that a running hash key can be computed from the previous * key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) /* =========================================================================== * Insert string str in the dictionary and set match_head to the previous head * of the hash chain (the most recent string with same hash key). Return * the previous length of the hash chain. * If this file is compiled with -DFASTEST, the compression level is forced * to 1, and no hash chains are maintained. * IN assertion: all calls to INSERT_STRING are made with consecutive input * characters and the first MIN_MATCH bytes of str are valid (except for * the last MIN_MATCH-1 bytes of the input file). */ #ifdef FASTEST #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #else #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #endif /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. */ #define CLEAR_HASH(s) \ s->head[s->hash_size-1] = NIL; \ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); /* =========================================================================== * Slide the hash table when sliding the window down (could be avoided with 32 * bit values at the expense of memory usage). We slide even when level == 0 to * keep the hash table consistent if we switch back to level > 0 later. */ local void slide_hash(s) deflate_state *s; { unsigned n, m; Posf *p; uInt wsize = s->w_size; n = s->hash_size; p = &s->head[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m - wsize : NIL); } while (--n); n = wsize; #ifndef FASTEST p = &s->prev[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m - wsize : NIL); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); #endif } /* ========================================================================= */ int ZEXPORT deflateInit_(strm, level, version, stream_size) z_streamp strm; int level; const char *version; int stream_size; { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, version, stream_size) z_streamp strm; int level; int method; int windowBits; int memLevel; int strategy; const char *version; int stream_size; { deflate_state *s; int wrap = 1; static const char my_version[] = ZLIB_VERSION; ushf *overlay; /* We overlay pending_buf and d_buf+l_buf. This works since the average * output size for (length,distance) codes is <= 24 bits. */ if (version == Z_NULL || version[0] != my_version[0] || stream_size != sizeof(z_stream)) { return Z_VERSION_ERROR; } if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; windowBits = -windowBits; } #ifdef GZIP else if (windowBits > 15) { wrap = 2; /* write gzip wrapper instead */ windowBits -= 16; } #endif if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) { return Z_STREAM_ERROR; } if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; s->status = INIT_STATE; /* to pass state test in deflateReset() */ s->wrap = wrap; s->gzhead = Z_NULL; s->w_bits = (uInt)windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; s->hash_bits = (uInt)memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->high_water = 0; /* nothing written to s->window yet */ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); s->pending_buf = (uchf *) overlay; s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { s->status = FINISH_STATE; strm->msg = ERR_MSG(Z_MEM_ERROR); deflateEnd (strm); return Z_MEM_ERROR; } s->d_buf = overlay + s->lit_bufsize/sizeof(ush); s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; s->level = level; s->strategy = strategy; s->method = (Byte)method; return deflateReset(strm); } /* ========================================================================= * Check for a valid deflate stream state. Return 0 if ok, 1 if not. */ local int deflateStateCheck (strm) z_streamp strm; { deflate_state *s; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) return 1; s = strm->state; if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE && #ifdef GZIP s->status != GZIP_STATE && #endif s->status != EXTRA_STATE && s->status != NAME_STATE && s->status != COMMENT_STATE && s->status != HCRC_STATE && s->status != BUSY_STATE && s->status != FINISH_STATE)) return 1; return 0; } /* ========================================================================= */ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { deflate_state *s; uInt str, n; int wrap; unsigned avail; z_const unsigned char *next; if (deflateStateCheck(strm) || dictionary == Z_NULL) return Z_STREAM_ERROR; s = strm->state; wrap = s->wrap; if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) return Z_STREAM_ERROR; /* when using zlib wrappers, compute Adler-32 for provided dictionary */ if (wrap == 1) strm->adler = adler32(strm->adler, dictionary, dictLength); s->wrap = 0; /* avoid computing Adler-32 in read_buf */ /* if dictionary would fill window, just replace the history */ if (dictLength >= s->w_size) { if (wrap == 0) { /* already empty otherwise */ CLEAR_HASH(s); s->strstart = 0; s->block_start = 0L; s->insert = 0; } dictionary += dictLength - s->w_size; /* use the tail */ dictLength = s->w_size; } /* insert dictionary into window and hash */ avail = strm->avail_in; next = strm->next_in; strm->avail_in = dictLength; strm->next_in = (z_const Bytef *)dictionary; fill_window(s); while (s->lookahead >= MIN_MATCH) { str = s->strstart; n = s->lookahead - (MIN_MATCH-1); do { UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); #ifndef FASTEST s->prev[str & s->w_mask] = s->head[s->ins_h]; #endif s->head[s->ins_h] = (Pos)str; str++; } while (--n); s->strstart = str; s->lookahead = MIN_MATCH-1; fill_window(s); } s->strstart += s->lookahead; s->block_start = (long)s->strstart; s->insert = s->lookahead; s->lookahead = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; strm->next_in = next; strm->avail_in = avail; s->wrap = wrap; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength) z_streamp strm; Bytef *dictionary; uInt *dictLength; { deflate_state *s; uInt len; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; len = s->strstart + s->lookahead; if (len > s->w_size) len = s->w_size; if (dictionary != Z_NULL && len) zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len); if (dictLength != Z_NULL) *dictLength = len; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateResetKeep (strm) z_streamp strm; { deflate_state *s; if (deflateStateCheck(strm)) { return Z_STREAM_ERROR; } strm->total_in = strm->total_out = 0; strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ strm->data_type = Z_UNKNOWN; s = (deflate_state *)strm->state; s->pending = 0; s->pending_out = s->pending_buf; if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ } s->status = #ifdef GZIP s->wrap == 2 ? GZIP_STATE : #endif s->wrap ? INIT_STATE : BUSY_STATE; strm->adler = #ifdef GZIP s->wrap == 2 ? crc32(0L, Z_NULL, 0) : #endif adler32(0L, Z_NULL, 0); s->last_flush = Z_NO_FLUSH; _tr_init(s); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateReset (strm) z_streamp strm; { int ret; ret = deflateResetKeep(strm); if (ret == Z_OK) lm_init(strm->state); return ret; } /* ========================================================================= */ int ZEXPORT deflateSetHeader (strm, head) z_streamp strm; gz_headerp head; { if (deflateStateCheck(strm) || strm->state->wrap != 2) return Z_STREAM_ERROR; strm->state->gzhead = head; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePending (strm, pending, bits) unsigned *pending; int *bits; z_streamp strm; { if (deflateStateCheck(strm)) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; if (bits != Z_NULL) *bits = strm->state->bi_valid; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePrime (strm, bits, value) z_streamp strm; int bits; int value; { deflate_state *s; int put; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; do { put = Buf_size - s->bi_valid; if (put > bits) put = bits; s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); s->bi_valid += put; _tr_flush_bits(s); value >>= put; bits -= put; } while (bits); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateParams(strm, level, strategy) z_streamp strm; int level; int strategy; { deflate_state *s; compress_func func; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && s->high_water) { /* Flush the last buffer: */ int err = deflate(strm, Z_BLOCK); if (err == Z_STREAM_ERROR) return err; if (strm->avail_out == 0) return Z_BUF_ERROR; } if (s->level != level) { if (s->level == 0 && s->matches != 0) { if (s->matches == 1) slide_hash(s); else CLEAR_HASH(s); s->matches = 0; } s->level = level; s->max_lazy_match = configuration_table[level].max_lazy; s->good_match = configuration_table[level].good_length; s->nice_match = configuration_table[level].nice_length; s->max_chain_length = configuration_table[level].max_chain; } s->strategy = strategy; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) z_streamp strm; int good_length; int max_lazy; int nice_length; int max_chain; { deflate_state *s; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; s->good_match = (uInt)good_length; s->max_lazy_match = (uInt)max_lazy; s->nice_match = nice_length; s->max_chain_length = (uInt)max_chain; return Z_OK; } /* ========================================================================= * For the default windowBits of 15 and memLevel of 8, this function returns * a close to exact, as well as small, upper bound on the compressed size. * They are coded as constants here for a reason--if the #define's are * changed, then this function needs to be changed as well. The return * value for 15 and 8 only works for those exact settings. * * For any setting other than those defaults for windowBits and memLevel, * the value returned is a conservative worst case for the maximum expansion * resulting from using fixed blocks instead of stored blocks, which deflate * can emit on compressed data for some combinations of the parameters. * * This function could be more sophisticated to provide closer upper bounds for * every combination of windowBits and memLevel. But even the conservative * upper bound of about 14% expansion does not seem onerous for output buffer * allocation. */ uLong ZEXPORT deflateBound(strm, sourceLen) z_streamp strm; uLong sourceLen; { deflate_state *s; uLong complen, wraplen; /* conservative upper bound for compressed data */ complen = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; /* if can't get parameters, return conservative bound plus zlib wrapper */ if (deflateStateCheck(strm)) return complen + 6; /* compute wrapper length */ s = strm->state; switch (s->wrap) { case 0: /* raw deflate */ wraplen = 0; break; case 1: /* zlib wrapper */ wraplen = 6 + (s->strstart ? 4 : 0); break; #ifdef GZIP case 2: /* gzip wrapper */ wraplen = 18; if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ Bytef *str; if (s->gzhead->extra != Z_NULL) wraplen += 2 + s->gzhead->extra_len; str = s->gzhead->name; if (str != Z_NULL) do { wraplen++; } while (*str++); str = s->gzhead->comment; if (str != Z_NULL) do { wraplen++; } while (*str++); if (s->gzhead->hcrc) wraplen += 2; } break; #endif default: /* for compiler happiness */ wraplen = 6; } /* if not default parameters, return conservative bound */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) return complen + wraplen; /* default settings: return tight bound for that case */ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13 - 6 + wraplen; } /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ local void putShortMSB (s, b) deflate_state *s; uInt b; { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } /* ========================================================================= * Flush as much pending output as possible. All deflate() output, except for * some deflate_stored() output, goes through this function so some * applications may wish to modify it to avoid allocating a large * strm->next_out buffer and copying into it. (See also read_buf()). */ local void flush_pending(strm) z_streamp strm; { unsigned len; deflate_state *s = strm->state; _tr_flush_bits(s); len = s->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; zmemcpy(strm->next_out, s->pending_out, len); strm->next_out += len; s->pending_out += len; strm->total_out += len; strm->avail_out -= len; s->pending -= len; if (s->pending == 0) { s->pending_out = s->pending_buf; } } /* =========================================================================== * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1]. */ #define HCRC_UPDATE(beg) \ do { \ if (s->gzhead->hcrc && s->pending > (beg)) \ strm->adler = crc32(strm->adler, s->pending_buf + (beg), \ s->pending - (beg)); \ } while (0) /* ========================================================================= */ int ZEXPORT deflate (strm, flush) z_streamp strm; int flush; { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; if (strm->next_out == Z_NULL || (strm->avail_in != 0 && strm->next_in == Z_NULL) || (s->status == FINISH_STATE && flush != Z_FINISH)) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); old_flush = s->last_flush; s->last_flush = flush; /* Flush as much pending output as possible */ if (s->pending != 0) { flush_pending(strm); if (strm->avail_out == 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won't be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s->last_flush = -1; return Z_OK; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && flush != Z_FINISH) { ERR_RETURN(strm, Z_BUF_ERROR); } /* User must not provide more input after the first FINISH: */ if (s->status == FINISH_STATE && strm->avail_in != 0) { ERR_RETURN(strm, Z_BUF_ERROR); } /* Write the header */ if (s->status == INIT_STATE) { /* zlib header */ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; uInt level_flags; if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) level_flags = 0; else if (s->level < 6) level_flags = 1; else if (s->level == 6) level_flags = 2; else level_flags = 3; header |= (level_flags << 6); if (s->strstart != 0) header |= PRESET_DICT; header += 31 - (header % 31); putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s->strstart != 0) { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } strm->adler = adler32(0L, Z_NULL, 0); s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } #ifdef GZIP if (s->status == GZIP_STATE) { /* gzip header */ strm->adler = crc32(0L, Z_NULL, 0); put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); if (s->gzhead == Z_NULL) { put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, OS_CODE); s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } else { put_byte(s, (s->gzhead->text ? 1 : 0) + (s->gzhead->hcrc ? 2 : 0) + (s->gzhead->extra == Z_NULL ? 0 : 4) + (s->gzhead->name == Z_NULL ? 0 : 8) + (s->gzhead->comment == Z_NULL ? 0 : 16) ); put_byte(s, (Byte)(s->gzhead->time & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, s->gzhead->os & 0xff); if (s->gzhead->extra != Z_NULL) { put_byte(s, s->gzhead->extra_len & 0xff); put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } if (s->gzhead->hcrc) strm->adler = crc32(strm->adler, s->pending_buf, s->pending); s->gzindex = 0; s->status = EXTRA_STATE; } } if (s->status == EXTRA_STATE) { if (s->gzhead->extra != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex; while (s->pending + left > s->pending_buf_size) { uInt copy = s->pending_buf_size - s->pending; zmemcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, copy); s->pending = s->pending_buf_size; HCRC_UPDATE(beg); s->gzindex += copy; flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; left -= copy; } zmemcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, left); s->pending += left; HCRC_UPDATE(beg); s->gzindex = 0; } s->status = NAME_STATE; } if (s->status == NAME_STATE) { if (s->gzhead->name != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { HCRC_UPDATE(beg); flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; } val = s->gzhead->name[s->gzindex++]; put_byte(s, val); } while (val != 0); HCRC_UPDATE(beg); s->gzindex = 0; } s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { if (s->gzhead->comment != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { HCRC_UPDATE(beg); flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; } val = s->gzhead->comment[s->gzindex++]; put_byte(s, val); } while (val != 0); HCRC_UPDATE(beg); } s->status = HCRC_STATE; } if (s->status == HCRC_STATE) { if (s->gzhead->hcrc) { if (s->pending + 2 > s->pending_buf_size) { flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); strm->adler = crc32(0L, Z_NULL, 0); } s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } #endif /* Start a new block or continue the current one. */ if (strm->avail_in != 0 || s->lookahead != 0 || (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; bstate = s->level == 0 ? deflate_stored(s, flush) : s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : s->strategy == Z_RLE ? deflate_rle(s, flush) : (*(configuration_table[s->level].func))(s, flush); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; } if (bstate == need_more || bstate == finish_started) { if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate == block_done) { if (flush == Z_PARTIAL_FLUSH) { _tr_align(s); } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(s); /* forget history */ if (s->lookahead == 0) { s->strstart = 0; s->block_start = 0L; s->insert = 0; } } } flush_pending(strm); if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK; } } } if (flush != Z_FINISH) return Z_OK; if (s->wrap <= 0) return Z_STREAM_END; /* Write the trailer */ #ifdef GZIP if (s->wrap == 2) { put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); put_byte(s, (Byte)(strm->total_in & 0xff)); put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); } else #endif { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ return s->pending != 0 ? Z_OK : Z_STREAM_END; } /* ========================================================================= */ int ZEXPORT deflateEnd (strm) z_streamp strm; { int status; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; status = strm->state->status; /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, strm->state->pending_buf); TRY_FREE(strm, strm->state->head); TRY_FREE(strm, strm->state->prev); TRY_FREE(strm, strm->state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; } /* ========================================================================= * Copy the source state to the destination state. * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ int ZEXPORT deflateCopy (dest, source) z_streamp dest; z_streamp source; { #ifdef MAXSEG_64K return Z_STREAM_ERROR; #else deflate_state *ds; deflate_state *ss; ushf *overlay; if (deflateStateCheck(source) || dest == Z_NULL) { return Z_STREAM_ERROR; } ss = source->state; zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); if (ds == Z_NULL) return Z_MEM_ERROR; dest->state = (struct internal_state FAR *) ds; zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); ds->strm = dest; ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); ds->pending_buf = (uchf *) overlay; if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { deflateEnd (dest); return Z_MEM_ERROR; } /* following zmemcpy do not work for 16-bit MSDOS */ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; ds->bl_desc.dyn_tree = ds->bl_tree; return Z_OK; #endif /* MAXSEG_64K */ } /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->next_in buffer and copying from it. * (See also flush_pending()). */ local unsigned read_buf(strm, buf, size) z_streamp strm; Bytef *buf; unsigned size; { unsigned len = strm->avail_in; if (len > size) len = size; if (len == 0) return 0; strm->avail_in -= len; zmemcpy(buf, strm->next_in, len); if (strm->state->wrap == 1) { strm->adler = adler32(strm->adler, buf, len); } #ifdef GZIP else if (strm->state->wrap == 2) { strm->adler = crc32(strm->adler, buf, len); } #endif strm->next_in += len; strm->total_in += len; return len; } /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ local void lm_init (s) deflate_state *s; { s->window_size = (ulg)2L*s->w_size; CLEAR_HASH(s); /* Set the default configuration parameters: */ s->max_lazy_match = configuration_table[s->level].max_lazy; s->good_match = configuration_table[s->level].good_length; s->nice_match = configuration_table[s->level].nice_length; s->max_chain_length = configuration_table[s->level].max_chain; s->strstart = 0; s->block_start = 0L; s->lookahead = 0; s->insert = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; #ifndef FASTEST #ifdef ASMV match_init(); /* initialize the asm code */ #endif #endif } #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ #ifndef ASMV /* For 80x86 and 680x0, an optimized version will be provided in match.asm or * match.S. The code will be functionally equivalent. */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ int best_len = (int)s->prev_length; /* best match length so far */ int nice_match = s->nice_match; /* stop if match long enough */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ Posf *prev = s->prev; uInt wmask = s->w_mask; #ifdef UNALIGNED_OK /* Compare two bytes at a time. Note: this is not always beneficial. * Try with and without -DUNALIGNED_OK to check. */ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; register ush scan_start = *(ushf*)scan; register ush scan_end = *(ushf*)(scan+best_len-1); #else register Bytef *strend = s->window + s->strstart + MAX_MATCH; register Byte scan_end1 = scan[best_len-1]; register Byte scan_end = scan[best_len]; #endif /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s->prev_length >= s->good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead; Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below * for insufficient lookahead only occur occasionally for performance * reasons. Therefore uninitialized memory will be accessed, and * conditional jumps will be made that depend on those values. * However the length of the match is limited to the lookahead, so * the output of deflate is not affected by the uninitialized values. */ #if (defined(UNALIGNED_OK) && MAX_MATCH == 258) /* This code assumes sizeof(unsigned short) == 2. Do not use * UNALIGNED_OK if your compiler uses a different size. */ if (*(ushf*)(match+best_len-1) != scan_end || *(ushf*)match != scan_start) continue; /* It is not necessary to compare scan[2] and match[2] since they are * always equal when the other bytes match, given that the hash keys * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at * strstart+3, +5, ... up to strstart+257. We check for insufficient * lookahead only every 4th comparison; the 128th check will be made * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ Assert(scan[2] == match[2], "scan[2]?"); scan++, match++; do { } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && scan < strend); /* The funny "do {}" generates better code on most compilers */ /* Here, scan <= window+strstart+257 */ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); if (*scan == *match) scan++; len = (MAX_MATCH - 1) - (int)(strend-scan); scan = strend - (MAX_MATCH-1); #else /* UNALIGNED_OK */ if (match[best_len] != scan_end || match[best_len-1] != scan_end1 || *match != *scan || *++match != scan[1]) continue; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); scan = strend - MAX_MATCH; #endif /* UNALIGNED_OK */ if (len > best_len) { s->match_start = cur_match; best_len = len; if (len >= nice_match) break; #ifdef UNALIGNED_OK scan_end = *(ushf*)(scan+best_len-1); #else scan_end1 = scan[best_len-1]; scan_end = scan[best_len]; #endif } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length != 0); if ((uInt)best_len <= s->lookahead) return (uInt)best_len; return s->lookahead; } #endif /* ASMV */ #else /* FASTEST */ /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ register Bytef *strend = s->window + s->strstart + MAX_MATCH; /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Return failure if the match length is less than 2: */ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match += 2; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); if (len < MIN_MATCH) return MIN_MATCH - 1; s->match_start = cur_match; return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; } #endif /* FASTEST */ #ifdef ZLIB_DEBUG #define EQUAL 0 /* result of memcmp for equal strings */ /* =========================================================================== * Check that the match at match_start is indeed a match. */ local void check_match(s, start, match, length) deflate_state *s; IPos start, match; int length; { /* check that the match is indeed a match */ if (zmemcmp(s->window + match, s->window + start, length) != EQUAL) { fprintf(stderr, " start %u, match %u, length %d\n", start, match, length); do { fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); } while (--length != 0); z_error("invalid match"); } if (z_verbose > 1) { fprintf(stderr,"\\[%d,%d]", start-match, length); do { putc(s->window[start++], stderr); } while (--length != 0); } } #else # define check_match(s, start, match, length) #endif /* ZLIB_DEBUG */ /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ local void fill_window(s) deflate_state *s; { unsigned n; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); /* Deal with !@#$% 64K limit: */ if (sizeof(int) <= 2) { if (more == 0 && s->strstart == 0 && s->lookahead == 0) { more = wsize; } else if (more == (unsigned)(-1)) { /* Very unlikely, but possible on 16 bit machine if * strstart == 0 && lookahead == 1 (input done a byte at time) */ more--; } } /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ if (s->strstart >= wsize+MAX_DIST(s)) { zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; slide_hash(s); more += wsize; } if (s->strm->avail_in == 0) break; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ Assert(more >= 2, "more < 2"); n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); s->lookahead += n; /* Initialize the hash value now that we have some input: */ if (s->lookahead + s->insert >= MIN_MATCH) { uInt str = s->strstart - s->insert; s->ins_h = s->window[str]; UPDATE_HASH(s, s->ins_h, s->window[str + 1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif while (s->insert) { UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); #ifndef FASTEST s->prev[str & s->w_mask] = s->head[s->ins_h]; #endif s->head[s->ins_h] = (Pos)str; str++; s->insert--; if (s->lookahead + s->insert < MIN_MATCH) break; } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); /* If the WIN_INIT bytes after the end of the current data have never been * written, then zero those bytes in order to avoid memory check reports of * the use of uninitialized (or uninitialised as Julian writes) bytes by * the longest match routines. Update the high water mark for the next * time through here. WIN_INIT is set to MAX_MATCH since the longest match * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. */ if (s->high_water < s->window_size) { ulg curr = s->strstart + (ulg)(s->lookahead); ulg init; if (s->high_water < curr) { /* Previous high water mark below current data -- zero WIN_INIT * bytes or up to end of window, whichever is less. */ init = s->window_size - curr; if (init > WIN_INIT) init = WIN_INIT; zmemzero(s->window + curr, (unsigned)init); s->high_water = curr + init; } else if (s->high_water < (ulg)curr + WIN_INIT) { /* High water mark at or above current data, but below current data * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up * to end of window, whichever is less. */ init = (ulg)curr + WIN_INIT - s->high_water; if (init > s->window_size - s->high_water) init = s->window_size - s->high_water; zmemzero(s->window + s->high_water, (unsigned)init); s->high_water += init; } } Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, "not enough room for search"); } /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ #define FLUSH_BLOCK_ONLY(s, last) { \ _tr_flush_block(s, (s->block_start >= 0L ? \ (charf *)&s->window[(unsigned)s->block_start] : \ (charf *)Z_NULL), \ (ulg)((long)s->strstart - s->block_start), \ (last)); \ s->block_start = s->strstart; \ flush_pending(s->strm); \ Tracev((stderr,"[FLUSH]")); \ } /* Same but force premature exit if necessary. */ #define FLUSH_BLOCK(s, last) { \ FLUSH_BLOCK_ONLY(s, last); \ if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ } /* Maximum stored block length in deflate format (not including header). */ #define MAX_STORED 65535 /* Minimum of a and b. */ #define MIN(a, b) ((a) > (b) ? (b) : (a)) /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * * In case deflateParams() is used to later switch to a non-zero compression * level, s->matches (otherwise unused when storing) keeps track of the number * of hash table slides to perform. If s->matches is 1, then one hash table * slide will be done when switching. If s->matches is 2, the maximum value * allowed here, then the hash table will be cleared, since two or more slides * is the same as a clear. * * deflate_stored() is written to minimize the number of times an input byte is * copied. It is most efficient with large input and output buffers, which * maximizes the opportunites to have a single copy from next_in to next_out. */ local block_state deflate_stored(s, flush) deflate_state *s; int flush; { /* Smallest worthy block size when not flushing or finishing. By default * this is 32K. This can be as small as 507 bytes for memLevel == 1. For * large input and output buffers, the stored block size will be larger. */ unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size); /* Copy as many min_block or larger stored blocks directly to next_out as * possible. If flushing, copy the remaining available input to next_out as * stored blocks, if there is enough space. */ unsigned len, left, have, last = 0; unsigned used = s->strm->avail_in; do { /* Set len to the maximum size block that we can copy directly with the * available input data and output space. Set left to how much of that * would be copied from what's left in the window. */ len = MAX_STORED; /* maximum deflate stored block length */ have = (s->bi_valid + 42) >> 3; /* number of header bytes */ if (s->strm->avail_out < have) /* need room for header */ break; /* maximum stored block length that will fit in avail_out: */ have = s->strm->avail_out - have; left = s->strstart - s->block_start; /* bytes left in window */ if (len > (ulg)left + s->strm->avail_in) len = left + s->strm->avail_in; /* limit len to the input */ if (len > have) len = have; /* limit len to the output */ /* If the stored block would be less than min_block in length, or if * unable to copy all of the available input when flushing, then try * copying to the window and the pending buffer instead. Also don't * write an empty block when flushing -- deflate() does that. */ if (len < min_block && ((len == 0 && flush != Z_FINISH) || flush == Z_NO_FLUSH || len != left + s->strm->avail_in)) break; /* Make a dummy stored block in pending to get the header bytes, * including any pending bits. This also updates the debugging counts. */ last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0; _tr_stored_block(s, (char *)0, 0L, last); /* Replace the lengths in the dummy stored block with len. */ s->pending_buf[s->pending - 4] = len; s->pending_buf[s->pending - 3] = len >> 8; s->pending_buf[s->pending - 2] = ~len; s->pending_buf[s->pending - 1] = ~len >> 8; /* Write the stored block header bytes. */ flush_pending(s->strm); #ifdef ZLIB_DEBUG /* Update debugging counts for the data about to be copied. */ s->compressed_len += len << 3; s->bits_sent += len << 3; #endif /* Copy uncompressed bytes from the window to next_out. */ if (left) { if (left > len) left = len; zmemcpy(s->strm->next_out, s->window + s->block_start, left); s->strm->next_out += left; s->strm->avail_out -= left; s->strm->total_out += left; s->block_start += left; len -= left; } /* Copy uncompressed bytes directly from next_in to next_out, updating * the check value. */ if (len) { read_buf(s->strm, s->strm->next_out, len); s->strm->next_out += len; s->strm->avail_out -= len; s->strm->total_out += len; } } while (last == 0); /* Update the sliding window with the last s->w_size bytes of the copied * data, or append all of the copied data to the existing window if less * than s->w_size bytes were copied. Also update the number of bytes to * insert in the hash tables, in the event that deflateParams() switches to * a non-zero compression level. */ used -= s->strm->avail_in; /* number of input bytes directly copied */ if (used) { /* If any input was used, then no unused input remains in the window, * therefore s->block_start == s->strstart. */ if (used >= s->w_size) { /* supplant the previous history */ s->matches = 2; /* clear hash */ zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); s->strstart = s->w_size; } else { if (s->window_size - s->strstart <= used) { /* Slide the window down. */ s->strstart -= s->w_size; zmemcpy(s->window, s->window + s->w_size, s->strstart); if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ } zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); s->strstart += used; } s->block_start = s->strstart; s->insert += MIN(used, s->w_size - s->insert); } if (s->high_water < s->strstart) s->high_water = s->strstart; /* If the last block was written to next_out, then done. */ if (last) return finish_done; /* If flushing and all input has been consumed, then done. */ if (flush != Z_NO_FLUSH && flush != Z_FINISH && s->strm->avail_in == 0 && (long)s->strstart == s->block_start) return block_done; /* Fill the window with any remaining input. */ have = s->window_size - s->strstart - 1; if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { /* Slide the window down. */ s->block_start -= s->w_size; s->strstart -= s->w_size; zmemcpy(s->window, s->window + s->w_size, s->strstart); if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ have += s->w_size; /* more space now */ } if (have > s->strm->avail_in) have = s->strm->avail_in; if (have) { read_buf(s->strm, s->window + s->strstart, have); s->strstart += have; } if (s->high_water < s->strstart) s->high_water = s->strstart; /* There was not enough avail_out to write a complete worthy or flushed * stored block to next_out. Write a stored block to pending instead, if we * have enough input for a worthy block, or if flushing and there is enough * room for the remaining input as a stored block in the pending buffer. */ have = (s->bi_valid + 42) >> 3; /* number of header bytes */ /* maximum stored block length that will fit in pending: */ have = MIN(s->pending_buf_size - have, MAX_STORED); min_block = MIN(have, s->w_size); left = s->strstart - s->block_start; if (left >= min_block || ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH && s->strm->avail_in == 0 && left <= have)) { len = MIN(left, have); last = flush == Z_FINISH && s->strm->avail_in == 0 && len == left ? 1 : 0; _tr_stored_block(s, (charf *)s->window + s->block_start, len, last); s->block_start += len; flush_pending(s->strm); } /* We've done all we can with the available input and output. */ return last ? finish_started : need_more; } /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ local block_state deflate_fast(s, flush) deflate_state *s; int flush; { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ } if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->match_start, s->match_length); _tr_tally_dist(s, s->strstart - s->match_start, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ #ifndef FASTEST if (s->match_length <= s->max_insert_length && s->lookahead >= MIN_MATCH) { s->match_length--; /* string at strstart already in table */ do { s->strstart++; INSERT_STRING(s, s->strstart, hash_head); /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s->match_length != 0); s->strstart++; } else #endif { s->strstart += s->match_length; s->match_length = 0; s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } #ifndef FASTEST /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ local block_state deflate_slow(s, flush) deflate_state *s; int flush; { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. */ s->prev_length = s->match_length, s->prev_match = s->match_start; s->match_length = MIN_MATCH-1; if (hash_head != NIL && s->prev_length < s->max_lazy_match && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ if (s->match_length <= 5 && (s->strategy == Z_FILTERED #if TOO_FAR <= 32767 || (s->match_length == MIN_MATCH && s->strstart - s->match_start > TOO_FAR) #endif )) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s->match_length = MIN_MATCH-1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ check_match(s, s->strstart-1, s->prev_match, s->prev_length); _tr_tally_dist(s, s->strstart -1 - s->prev_match, s->prev_length - MIN_MATCH, bflush); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s->lookahead -= s->prev_length-1; s->prev_length -= 2; do { if (++s->strstart <= max_insert) { INSERT_STRING(s, s->strstart, hash_head); } } while (--s->prev_length != 0); s->match_available = 0; s->match_length = MIN_MATCH-1; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } else if (s->match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally_lit(s, s->window[s->strstart-1], bflush); if (bflush) { FLUSH_BLOCK_ONLY(s, 0); } s->strstart++; s->lookahead--; if (s->strm->avail_out == 0) return need_more; } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s->match_available = 1; s->strstart++; s->lookahead--; } } Assert (flush != Z_NO_FLUSH, "no flush?"); if (s->match_available) { Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally_lit(s, s->window[s->strstart-1], bflush); s->match_available = 0; } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } #endif /* FASTEST */ /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ local block_state deflate_rle(s, flush) deflate_state *s; int flush; { int bflush; /* set if current block must be flushed */ uInt prev; /* byte at distance one to match */ Bytef *scan, *strend; /* scan goes up to strend for length of run */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the longest run, plus one for the unrolled loop. */ if (s->lookahead <= MAX_MATCH) { fill_window(s); if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* See how many times the previous byte repeats */ s->match_length = 0; if (s->lookahead >= MIN_MATCH && s->strstart > 0) { scan = s->window + s->strstart - 1; prev = *scan; if (prev == *++scan && prev == *++scan && prev == *++scan) { strend = s->window + s->strstart + MAX_MATCH; do { } while (prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && scan < strend); s->match_length = MAX_MATCH - (uInt)(strend - scan); if (s->match_length > s->lookahead) s->match_length = s->lookahead; } Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->strstart - 1, s->match_length); _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; s->strstart += s->match_length; s->match_length = 0; } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } /* =========================================================================== * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ local block_state deflate_huff(s, flush) deflate_state *s; int flush; { int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we have a literal to write. */ if (s->lookahead == 0) { fill_window(s); if (s->lookahead == 0) { if (flush == Z_NO_FLUSH) return need_more; break; /* flush the current block */ } } /* Output a literal byte */ s->match_length = 0; Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->last_lit) FLUSH_BLOCK(s, 0); return block_done; } ================================================ FILE: deps/CascLib/src/zlib/deflate.h ================================================ /* deflate.h -- internal compression state * Copyright (C) 1995-2016 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef DEFLATE_H #define DEFLATE_H #include "zutil.h" /* define NO_GZIP when compiling if you want to disable gzip header and trailer creation by deflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip encoding should be left enabled. */ #ifndef NO_GZIP # define GZIP #endif /* =========================================================================== * Internal compression state. */ #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ #define LITERALS 256 /* number of literal bytes 0..255 */ #define L_CODES (LITERALS+1+LENGTH_CODES) /* number of Literal or Length codes, including the END_BLOCK code */ #define D_CODES 30 /* number of distance codes */ #define BL_CODES 19 /* number of codes used to transfer the bit lengths */ #define HEAP_SIZE (2*L_CODES+1) /* maximum heap size */ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ #define Buf_size 16 /* size of bit buffer in bi_buf */ #define INIT_STATE 42 /* zlib header -> BUSY_STATE */ #ifdef GZIP # define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ #endif #define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ #define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ #define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ #define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ #define BUSY_STATE 113 /* deflate -> FINISH_STATE */ #define FINISH_STATE 666 /* stream complete */ /* Stream status */ /* Data structure describing a single value and its code string. */ typedef struct ct_data_s { union { ush freq; /* frequency count */ ush code; /* bit string */ } fc; union { ush dad; /* father node in Huffman tree */ ush len; /* length of bit string */ } dl; } FAR ct_data; #define Freq fc.freq #define Code fc.code #define Dad dl.dad #define Len dl.len typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ const static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; typedef Pos FAR Posf; typedef unsigned IPos; /* A Pos is an index in the character window. We use short instead of int to * save space in the various tables. IPos is used only for parameter passing. */ typedef struct internal_state { z_streamp strm; /* pointer back to this zlib stream */ int status; /* as the name implies */ Bytef *pending_buf; /* output still pending */ ulg pending_buf_size; /* size of pending_buf */ Bytef *pending_out; /* next pending byte to output to the stream */ ulg pending; /* nb of bytes in the pending buffer */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ ulg gzindex; /* where in extra, name, or comment */ Byte method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ /* used by deflate.c: */ uInt w_size; /* LZ77 window size (32K by default) */ uInt w_bits; /* log2(w_size) (8..16) */ uInt w_mask; /* w_size - 1 */ Bytef *window; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. Also, it limits * the window size to 64K, which is quite useful on MSDOS. * To do: use the user input buffer as sliding window. */ ulg window_size; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ Posf *prev; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ Posf *head; /* Heads of the hash chains or NIL. */ uInt ins_h; /* hash index of string to be inserted */ uInt hash_size; /* number of elements in hash table */ uInt hash_bits; /* log2(hash_size) */ uInt hash_mask; /* hash_size-1 */ uInt hash_shift; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ long block_start; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ uInt match_length; /* length of best match */ IPos prev_match; /* previous match */ int match_available; /* set if previous match exists */ uInt strstart; /* start of string to insert */ uInt match_start; /* start of matching string */ uInt lookahead; /* number of valid bytes ahead in window */ uInt prev_length; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ uInt max_chain_length; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ uInt max_lazy_match; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ # define max_insert_length max_lazy_match /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ int level; /* compression level (1..9) */ int strategy; /* favor or force Huffman coding*/ uInt good_match; /* Use a faster search when the previous match is longer than this */ int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn't use ct_data typedef below to suppress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ struct tree_desc_s l_desc; /* desc. for literal tree */ struct tree_desc_s d_desc; /* desc. for distance tree */ struct tree_desc_s bl_desc; /* desc. for bit length tree */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ int heap_len; /* number of elements in the heap */ int heap_max; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ uch depth[2*L_CODES+1]; /* Depth of each subtree used as tie breaker for trees of equal frequency */ uchf *l_buf; /* buffer for literals or lengths */ uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can't count above 4 */ uInt last_lit; /* running index in l_buf */ ushf *d_buf; /* Buffer for distances. To simplify the code, d_buf and l_buf have * the same number of elements. To use different lengths, an extra flag * array would be necessary. */ ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ uInt matches; /* number of string matches in current block */ uInt insert; /* bytes at end of window left to insert */ #ifdef ZLIB_DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ #endif ush bi_buf; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ int bi_valid; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ ulg high_water; /* High water mark offset in window for initialized bytes -- bytes above * this are set to zero in order to avoid memory check warnings when * longest match routines access bytes past the input. This is then * updated to the new high water mark. */ } FAR deflate_state; /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ #define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ #define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) /* In order to simplify the code, particularly on 16 bit machines, match * distances are limited to MAX_DIST instead of WSIZE. */ #define WIN_INIT MAX_MATCH /* Number of bytes after end of data in window to initialize in order to avoid memory checker errors from longest match routines */ /* in trees.c */ void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) /* Mapping from a distance to a distance code. dist is the distance - 1 and * must not have side effects. _dist_code[256] and _dist_code[257] are never * used. */ #ifndef ZLIB_DEBUG /* Inline versions of _tr_tally for speed: */ #if defined(GEN_TREES_H) || !defined(STDC) extern uch ZLIB_INTERNAL _length_code[]; extern uch ZLIB_INTERNAL _dist_code[]; #else extern const uch ZLIB_INTERNAL _length_code[]; extern const uch ZLIB_INTERNAL _dist_code[]; #endif # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->d_buf[s->last_lit] = 0; \ s->l_buf[s->last_lit++] = cc; \ s->dyn_ltree[cc].Freq++; \ flush = (s->last_lit == s->lit_bufsize-1); \ } # define _tr_tally_dist(s, distance, length, flush) \ { uch len = (uch)(length); \ ush dist = (ush)(distance); \ s->d_buf[s->last_lit] = dist; \ s->l_buf[s->last_lit++] = len; \ dist--; \ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->last_lit == s->lit_bufsize-1); \ } #else # define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) # define _tr_tally_dist(s, distance, length, flush) \ flush = _tr_tally(s, distance, length) #endif #endif /* DEFLATE_H */ ================================================ FILE: deps/CascLib/src/zlib/gzguts.h ================================================ /* gzguts.h -- zlib internal header definitions for gz* operations * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #ifdef _LARGEFILE64_SOURCE # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE 1 # endif # ifdef _FILE_OFFSET_BITS # undef _FILE_OFFSET_BITS # endif #endif #ifdef HAVE_HIDDEN # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include #include "zlib.h" #ifdef STDC # include # include # include #endif #ifndef _POSIX_SOURCE # define _POSIX_SOURCE #endif #include #ifdef _WIN32 # include #endif #if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) # include #endif #if defined(_WIN32) || defined(__CYGWIN__) # define WIDECHAR #endif #ifdef WINAPI_FAMILY # define open _open # define read _read # define write _write # define close _close #endif #ifdef NO_DEFLATE /* for compatibility with old definition */ # define NO_GZCOMPRESS #endif #if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #if defined(__CYGWIN__) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #ifndef HAVE_VSNPRINTF # ifdef MSDOS /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), but for now we just assume it doesn't. */ # define NO_vsnprintf # endif # ifdef __TURBOC__ # define NO_vsnprintf # endif # ifdef WIN32 /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ # if !defined(vsnprintf) && !defined(NO_vsnprintf) # if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) # define vsnprintf _vsnprintf # endif # endif # endif # ifdef __SASC # define NO_vsnprintf # endif # ifdef VMS # define NO_vsnprintf # endif # ifdef __OS400__ # define NO_vsnprintf # endif # ifdef __MVS__ # define NO_vsnprintf # endif #endif /* unlike snprintf (which is required in C99), _snprintf does not guarantee null termination of the result -- however this is only used in gzlib.c where the result is assured to fit in the space provided */ #if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf _snprintf #endif #ifndef local # define local static #endif /* since "static" is used to mean two completely different things in C, we define "local" for the non-static meaning of "static", for readability (compile with -Dlocal if your debugger can't find static symbols) */ /* gz* functions always use library allocation functions */ #ifndef STDC extern voidp malloc OF((uInt size)); extern void free OF((voidpf ptr)); #endif /* get errno and strerror definition */ #if defined UNDER_CE # include # define zstrerror() gz_strwinerror((DWORD)GetLastError()) #else # ifndef NO_STRERROR # include # define zstrerror() strerror(errno) # else # define zstrerror() "stdio error (consult errno)" # endif #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); #endif /* default memLevel */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default i/o buffer size -- double this for output when reading (this and twice this must be able to fit in an unsigned type) */ #define GZBUFSIZE 8192 /* gzip modes, also provide a little integrity check on the passed structure */ #define GZ_NONE 0 #define GZ_READ 7247 #define GZ_WRITE 31153 #define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ /* values for gz_state how */ #define LOOK 0 /* look for a gzip header */ #define COPY 1 /* copy input directly */ #define GZIP 2 /* decompress a gzip stream */ /* internal gzip file state data structure */ typedef struct { /* exposed contents for gzgetc() macro */ struct gzFile_s x; /* "x" for exposed */ /* x.have: number of bytes available at x.next */ /* x.next: next output data to deliver or write */ /* x.pos: current position in uncompressed data */ /* used for both reading and writing */ int mode; /* see gzip modes above */ int fd; /* file descriptor */ char *path; /* path or fd for error messages */ unsigned size; /* buffer size, zero if not allocated yet */ unsigned want; /* requested buffer size, default is GZBUFSIZE */ unsigned char *in; /* input buffer (double-sized when writing) */ unsigned char *out; /* output buffer (double-sized when reading) */ int direct; /* 0 if processing gzip, 1 if transparent */ /* just for reading */ int how; /* 0: get header, 1: copy, 2: decompress */ z_off64_t start; /* where the gzip data started, for rewinding */ int eof; /* true if end of input file reached */ int past; /* true if read requested past end */ /* just for writing */ int level; /* compression level */ int strategy; /* compression strategy */ /* seek request */ z_off64_t skip; /* amount to skip (already rewound if backwards) */ int seek; /* true if seek request pending */ /* error information */ int err; /* error code */ char *msg; /* error message */ /* zlib inflate or deflate stream */ z_stream strm; /* stream structure in-place (not a pointer) */ } gz_state; typedef gz_state FAR *gz_statep; /* shared functions */ void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); #if defined UNDER_CE char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ #ifdef INT_MAX # define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) #else unsigned ZLIB_INTERNAL gz_intmax OF((void)); # define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) #endif ================================================ FILE: deps/CascLib/src/zlib/inffast.c ================================================ /* inffast.c -- fast decoding * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifdef ASMINF # pragma message("Assembler code may have bugs -- use at your own risk") #else /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state->mode == LEN strm->avail_in >= 6 strm->avail_out >= 258 start >= strm->avail_out state->bits < 8 On return, state->mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm->avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ void ZLIB_INTERNAL inflate_fast(strm, start) z_streamp strm; unsigned start; /* inflate()'s starting value for strm->avail_out */ { struct inflate_state FAR *state; z_const unsigned char FAR *in; /* local strm->next_in */ z_const unsigned char FAR *last; /* have enough input while in < last */ unsigned char FAR *out; /* local strm->next_out */ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ unsigned char FAR *end; /* while out < end, enough space available */ #ifdef INFLATE_STRICT unsigned dmax; /* maximum distance from zlib header */ #endif unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ unsigned long hold; /* local strm->hold */ unsigned bits; /* local strm->bits */ code const FAR *lcode; /* local strm->lencode */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ code here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ unsigned dist; /* match distance */ unsigned char FAR *from; /* where to copy match from */ /* copy state to local variables */ state = (struct inflate_state FAR *)strm->state; in = strm->next_in; last = in + (strm->avail_in - 5); out = strm->next_out; beg = out - (start - strm->avail_out); end = out + (strm->avail_out - 257); #ifdef INFLATE_STRICT dmax = state->dmax; #endif wsize = state->wsize; whave = state->whave; wnext = state->wnext; window = state->window; hold = state->hold; bits = state->bits; lcode = state->lencode; dcode = state->distcode; lmask = (1U << state->lenbits) - 1; dmask = (1U << state->distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ do { if (bits < 15) { hold += (unsigned long)(*in++) << bits; bits += 8; hold += (unsigned long)(*in++) << bits; bits += 8; } here = lcode[hold & lmask]; dolen: op = (unsigned)(here.bits); hold >>= op; bits -= op; op = (unsigned)(here.op); if (op == 0) { /* literal */ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); *out++ = (unsigned char)(here.val); } else if (op & 16) { /* length base */ len = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; } len += (unsigned)hold & ((1U << op) - 1); hold >>= op; bits -= op; } Tracevv((stderr, "inflate: length %u\n", len)); if (bits < 15) { hold += (unsigned long)(*in++) << bits; bits += 8; hold += (unsigned long)(*in++) << bits; bits += 8; } here = dcode[hold & dmask]; dodist: op = (unsigned)(here.bits); hold >>= op; bits -= op; op = (unsigned)(here.op); if (op & 16) { /* distance base */ dist = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; } } dist += (unsigned)hold & ((1U << op) - 1); #ifdef INFLATE_STRICT if (dist > dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif hold >>= op; bits -= op; Tracevv((stderr, "inflate: distance %u\n", dist)); op = (unsigned)(out - beg); /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (len <= op - whave) { do { *out++ = 0; } while (--len); continue; } len -= op - whave; do { *out++ = 0; } while (--op > whave); if (op == 0) { from = out - dist; do { *out++ = *from++; } while (--len); continue; } #endif } from = window; if (wnext == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } else if (wnext < op) { /* wrap around window */ from += wsize + wnext - op; op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { *out++ = *from++; } while (--op); from = window; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } } else { /* contiguous in window */ from += wnext - op; if (op < len) { /* some from window */ len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } while (len > 2) { *out++ = *from++; *out++ = *from++; *out++ = *from++; len -= 3; } if (len) { *out++ = *from++; if (len > 1) *out++ = *from++; } } else { from = out - dist; /* copy direct from output */ do { /* minimum length is three */ *out++ = *from++; *out++ = *from++; *out++ = *from++; len -= 3; } while (len > 2); if (len) { *out++ = *from++; if (len > 1) *out++ = *from++; } } } else if ((op & 64) == 0) { /* 2nd level distance code */ here = dcode[here.val + (hold & ((1U << op) - 1))]; goto dodist; } else { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } } else if ((op & 64) == 0) { /* 2nd level length code */ here = lcode[here.val + (hold & ((1U << op) - 1))]; goto dolen; } else if (op & 32) { /* end-of-block */ Tracevv((stderr, "inflate: end of block\n")); state->mode = TYPE; break; } else { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } } while (in < last && out < end); /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ len = bits >> 3; in -= len; bits -= len << 3; hold &= (1U << bits) - 1; /* update state and return */ strm->next_in = in; strm->next_out = out; strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_out = (unsigned)(out < end ? 257 + (end - out) : 257 - (out - end)); state->hold = hold; state->bits = bits; return; } /* inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - Using bit fields for code structure - Different op definition to avoid & for extra bits (do & for table bits) - Three separate decoding do-loops for direct, window, and wnext == 0 - Special case for distance > 1 copies to do overlapped load and store copy - Explicit branch predictions (based on measured branch probabilities) - Deferring match copy and interspersed it with decoding subsequent codes - Swapping literal/length else - Swapping window/direct else - Larger unrolled copy loops (three is about right) - Moving len -= 3 statement into middle of loop */ #endif /* !ASMINF */ ================================================ FILE: deps/CascLib/src/zlib/inffast.h ================================================ /* inffast.h -- header to use inffast.c * Copyright (C) 1995-2003, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); ================================================ FILE: deps/CascLib/src/zlib/inffixed.h ================================================ /* inffixed.h -- table for decoding fixed codes * Generated automatically by makefixed(). */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of this library and is subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, {0,9,255} }; static const code distfix[32] = { {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, {22,5,193},{64,5,0} }; ================================================ FILE: deps/CascLib/src/zlib/inflate.c ================================================ /* inflate.c -- zlib decompression * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * Change history: * * 1.2.beta0 24 Nov 2002 * - First version -- complete rewrite of inflate to simplify code, avoid * creation of window when not needed, minimize use of window when it is * needed, make inffast.c even faster, implement gzip decoding, and to * improve code readability and style over the previous zlib inflate code * * 1.2.beta1 25 Nov 2002 * - Use pointers for available input and output checking in inffast.c * - Remove input and output counters in inffast.c * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 * - Remove unnecessary second byte pull from length extra in inffast.c * - Unroll direct copy to three copies per loop in inffast.c * * 1.2.beta2 4 Dec 2002 * - Change external routine names to reduce potential conflicts * - Correct filename to inffixed.h for fixed tables in inflate.c * - Make hbuf[] unsigned char to match parameter type in inflate.c * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) * to avoid negation problem on Alphas (64 bit) in inflate.c * * 1.2.beta3 22 Dec 2002 * - Add comments on state->bits assertion in inffast.c * - Add comments on op field in inftrees.h * - Fix bug in reuse of allocated window after inflateReset() * - Remove bit fields--back to byte structure for speed * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths * - Change post-increments to pre-increments in inflate_fast(), PPC biased? * - Add compile time option, POSTINC, to use post-increments instead (Intel?) * - Make MATCH copy in inflate() much faster for when inflate_fast() not used * - Use local copies of stream next and avail values, as well as local bit * buffer and bit count in inflate()--for speed when inflate_fast() not used * * 1.2.beta4 1 Jan 2003 * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings * - Move a comment on output buffer sizes from inffast.c to inflate.c * - Add comments in inffast.c to introduce the inflate_fast() routine * - Rearrange window copies in inflate_fast() for speed and simplification * - Unroll last copy for window match in inflate_fast() * - Use local copies of window variables in inflate_fast() for speed * - Pull out common wnext == 0 case for speed in inflate_fast() * - Make op and len in inflate_fast() unsigned for consistency * - Add FAR to lcode and dcode declarations in inflate_fast() * - Simplified bad distance check in inflate_fast() * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new * source file infback.c to provide a call-back interface to inflate for * programs like gzip and unzip -- uses window as output buffer to avoid * window copying * * 1.2.beta5 1 Jan 2003 * - Improved inflateBack() interface to allow the caller to provide initial * input in strm. * - Fixed stored blocks bug in inflateBack() * * 1.2.beta6 4 Jan 2003 * - Added comments in inffast.c on effectiveness of POSTINC * - Typecasting all around to reduce compiler warnings * - Changed loops from while (1) or do {} while (1) to for (;;), again to * make compilers happy * - Changed type of window in inflateBackInit() to unsigned char * * * 1.2.beta7 27 Jan 2003 * - Changed many types to unsigned or unsigned short to avoid warnings * - Added inflateCopy() function * * 1.2.0 9 Mar 2003 * - Changed inflateBack() interface to provide separate opaque descriptors * for the in() and out() functions * - Changed inflateBack() argument and in_func typedef to swap the length * and buffer address return values for the input function * - Check next_in and next_out for Z_NULL on entry to inflate() * * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifdef MAKEFIXED # ifndef BUILDFIXED # define BUILDFIXED # endif #endif /* function prototypes */ local int inflateStateCheck OF((z_streamp strm)); local void fixedtables OF((struct inflate_state FAR *state)); local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, unsigned copy)); #ifdef BUILDFIXED void makefixed OF((void)); #endif local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, unsigned len)); local int inflateStateCheck(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) return 1; state = (struct inflate_state FAR *)strm->state; if (state == Z_NULL || state->strm != strm || state->mode < HEAD || state->mode > SYNC) return 1; return 0; } int ZEXPORT inflateResetKeep(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; if (state->wrap) /* to support ill-conceived Java test suite */ strm->adler = state->wrap & 1; state->mode = HEAD; state->last = 0; state->havedict = 0; state->dmax = 32768U; state->head = Z_NULL; state->hold = 0; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; state->sane = 1; state->back = -1; Tracev((stderr, "inflate: reset\n")); return Z_OK; } int ZEXPORT inflateReset(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; state->wsize = 0; state->whave = 0; state->wnext = 0; return inflateResetKeep(strm); } int ZEXPORT inflateReset2(strm, windowBits) z_streamp strm; int windowBits; { int wrap; struct inflate_state FAR *state; /* get the state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { wrap = 0; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 5; #ifdef GUNZIP if (windowBits < 48) windowBits &= 15; #endif } /* set number of window bits, free window if different */ if (windowBits && (windowBits < 8 || windowBits > 15)) return Z_STREAM_ERROR; if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { ZFREE(strm, state->window); state->window = Z_NULL; } /* update state and reset the rest of it */ state->wrap = wrap; state->wbits = (unsigned)windowBits; return inflateReset(strm); } int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) z_streamp strm; int windowBits; const char *version; int stream_size; { int ret; struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif state = (struct inflate_state FAR *) ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->strm = strm; state->window = Z_NULL; state->mode = HEAD; /* to pass state test in inflateReset2() */ ret = inflateReset2(strm, windowBits); if (ret != Z_OK) { ZFREE(strm, state); strm->state = Z_NULL; } return ret; } int ZEXPORT inflateInit_(strm, version, stream_size) z_streamp strm; const char *version; int stream_size; { return inflateInit2_(strm, DEF_WBITS, version, stream_size); } int ZEXPORT inflatePrime(strm, bits, value) z_streamp strm; int bits; int value; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; state->bits = 0; return Z_OK; } if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR; value &= (1L << bits) - 1; state->hold += (unsigned)value << state->bits; state->bits += (uInt)bits; return Z_OK; } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ local void fixedtables(state) struct inflate_state FAR *state; { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; static code fixed[544]; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { unsigned sym, bits; static code *next; /* literal/length table */ sym = 0; while (sym < 144) state->lens[sym++] = 8; while (sym < 256) state->lens[sym++] = 9; while (sym < 280) state->lens[sym++] = 7; while (sym < 288) state->lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); /* distance table */ sym = 0; while (sym < 32) state->lens[sym++] = 5; distfix = next; bits = 5; inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); /* do this just once */ virgin = 0; } #else /* !BUILDFIXED */ # include "inffixed.h" #endif /* BUILDFIXED */ state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } #ifdef MAKEFIXED #include /* Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also defines BUILDFIXED, so the tables are built on the fly. makefixed() writes those tables to stdout, which would be piped to inffixed.h. A small program can simply call makefixed to do this: void makefixed(void); int main(void) { makefixed(); return 0; } Then that can be linked with zlib built with MAKEFIXED defined and run: a.out > inffixed.h */ void makefixed() { unsigned low, size; struct inflate_state state; fixedtables(&state); puts(" /* inffixed.h -- table for decoding fixed codes"); puts(" * Generated automatically by makefixed()."); puts(" */"); puts(""); puts(" /* WARNING: this file should *not* be used by applications."); puts(" It is part of the implementation of this library and is"); puts(" subject to change. Applications should only use zlib.h."); puts(" */"); puts(""); size = 1U << 9; printf(" static const code lenfix[%u] = {", size); low = 0; for (;;) { if ((low % 7) == 0) printf("\n "); printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, state.lencode[low].bits, state.lencode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); size = 1U << 5; printf("\n static const code distfix[%u] = {", size); low = 0; for (;;) { if ((low % 6) == 0) printf("\n "); printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, state.distcode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); } #endif /* MAKEFIXED */ /* Update the window with the last wsize (normally 32K) bytes written before returning. If window does not exist yet, create it. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to create a window for dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ local int updatewindow(strm, end, copy) z_streamp strm; const Bytef *end; unsigned copy; { struct inflate_state FAR *state; unsigned dist; state = (struct inflate_state FAR *)strm->state; /* if it hasn't been done already, allocate space for the window */ if (state->window == Z_NULL) { state->window = (unsigned char FAR *) ZALLOC(strm, 1U << state->wbits, sizeof(unsigned char)); if (state->window == Z_NULL) return 1; } /* if window not in use yet, initialize */ if (state->wsize == 0) { state->wsize = 1U << state->wbits; state->wnext = 0; state->whave = 0; } /* copy state->wsize or less output bytes into the circular window */ if (copy >= state->wsize) { zmemcpy(state->window, end - state->wsize, state->wsize); state->wnext = 0; state->whave = state->wsize; } else { dist = state->wsize - state->wnext; if (dist > copy) dist = copy; zmemcpy(state->window + state->wnext, end - copy, dist); copy -= dist; if (copy) { zmemcpy(state->window, end - copy, copy); state->wnext = copy; state->whave = state->wsize; } else { state->wnext += dist; if (state->wnext == state->wsize) state->wnext = 0; if (state->whave < state->wsize) state->whave += dist; } } return 0; } /* Macros for inflate(): */ /* check function to use adler32() for zlib or crc32() for gzip */ #ifdef GUNZIP # define UPDATE(check, buf, len) \ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) #else # define UPDATE(check, buf, len) adler32(check, buf, len) #endif /* check macros for header crc */ #ifdef GUNZIP # define CRC2(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ check = crc32(check, hbuf, 2); \ } while (0) # define CRC4(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ hbuf[2] = (unsigned char)((word) >> 16); \ hbuf[3] = (unsigned char)((word) >> 24); \ check = crc32(check, hbuf, 4); \ } while (0) #endif /* Load registers with state in inflate() for speed */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Restore state from registers in inflate() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflate() if there is no input available. */ #define PULLBYTE() \ do { \ if (have == 0) goto inf_leave; \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflate(). */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* inflate() uses a state machine to process as much input data and generate as much output data as possible before returning. The state machine is structured roughly as follows: for (;;) switch (state) { ... case STATEn: if (not enough input data or output space to make progress) return; ... make progress ... state = STATEm; break; ... } so when inflate() is called again, the same case is attempted again, and if the appropriate resources are provided, the machine proceeds to the next state. The NEEDBITS() macro is usually the way the state evaluates whether it can proceed or should return. NEEDBITS() does the return if the requested bits are not available. The typical use of the BITS macros is: NEEDBITS(n); ... do something with BITS(n) ... DROPBITS(n); where NEEDBITS(n) either returns from inflate() if there isn't enough input left to load n bits into the accumulator, or it continues. BITS(n) gives the low n bits in the accumulator. When done, DROPBITS(n) drops the low n bits off the accumulator. INITBITS() clears the accumulator and sets the number of available bits to zero. BYTEBITS() discards just enough bits to put the accumulator on a byte boundary. After BYTEBITS() and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return if there is no input available. The decoding of variable length codes uses PULLBYTE() directly in order to pull just enough bytes to decode the next code, and no more. Some states loop until they get enough input, making sure that enough state information is maintained to continue the loop where it left off if NEEDBITS() returns in the loop. For example, want, need, and keep would all have to actually be part of the saved state in case NEEDBITS() returns: case STATEw: while (want < need) { NEEDBITS(n); keep[want++] = BITS(n); DROPBITS(n); } state = STATEx; case STATEx: As shown above, if the next state is also the next case, then the break is omitted. A state may also return if there is not enough output space available to complete that state. Those states are copying stored data, writing a literal byte, and copying a matching string. When returning, a "goto inf_leave" is used to update the total counters, update the check value, and determine whether any progress has been made during that inflate() call in order to return the proper return code. Progress is defined as a change in either strm->avail_in or strm->avail_out. When there is a window, goto inf_leave will update the window with the last output written. If a goto inf_leave occurs in the middle of decompression and there is no window currently, goto inf_leave will create one and copy output to the window for the next call of inflate(). In this implementation, the flush parameter of inflate() only affects the return code (per zlib.h). inflate() always writes as much as possible to strm->next_out, given the space available and the provided input--the effect documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers the allocation of and copying into a sliding window until necessary, which provides the effect documented in zlib.h for Z_FINISH when the entire input stream available. So the only thing the flush parameter actually does is: when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it will return Z_BUF_ERROR if it has not reached the end of the stream. */ int ZEXPORT inflate(strm, flush) z_streamp strm; int flush; { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned in, out; /* save starting available input and output */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ #ifdef GUNZIP unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ #endif static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; if (inflateStateCheck(strm) || strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ LOAD(); in = have; out = left; ret = Z_OK; for (;;) switch (state->mode) { case HEAD: if (state->wrap == 0) { state->mode = TYPEDO; break; } NEEDBITS(16); #ifdef GUNZIP if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ if (state->wbits == 0) state->wbits = 15; state->check = crc32(0L, Z_NULL, 0); CRC2(state->check, hold); INITBITS(); state->mode = FLAGS; break; } state->flags = 0; /* expect zlib header */ if (state->head != Z_NULL) state->head->done = -1; if (!(state->wrap & 1) || /* check if zlib header allowed */ #else if ( #endif ((BITS(8) << 8) + (hold >> 8)) % 31) { strm->msg = (char *)"incorrect header check"; state->mode = BAD; break; } if (BITS(4) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } DROPBITS(4); len = BITS(4) + 8; if (state->wbits == 0) state->wbits = len; if (len > 15 || len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; } state->dmax = 1U << len; Tracev((stderr, "inflate: zlib header ok\n")); strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = hold & 0x200 ? DICTID : TYPE; INITBITS(); break; #ifdef GUNZIP case FLAGS: NEEDBITS(16); state->flags = (int)(hold); if ((state->flags & 0xff) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } if (state->flags & 0xe000) { strm->msg = (char *)"unknown header flags set"; state->mode = BAD; break; } if (state->head != Z_NULL) state->head->text = (int)((hold >> 8) & 1); if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); state->mode = TIME; case TIME: NEEDBITS(32); if (state->head != Z_NULL) state->head->time = hold; if ((state->flags & 0x0200) && (state->wrap & 4)) CRC4(state->check, hold); INITBITS(); state->mode = OS; case OS: NEEDBITS(16); if (state->head != Z_NULL) { state->head->xflags = (int)(hold & 0xff); state->head->os = (int)(hold >> 8); } if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); state->mode = EXLEN; case EXLEN: if (state->flags & 0x0400) { NEEDBITS(16); state->length = (unsigned)(hold); if (state->head != Z_NULL) state->head->extra_len = (unsigned)hold; if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); } else if (state->head != Z_NULL) state->head->extra = Z_NULL; state->mode = EXTRA; case EXTRA: if (state->flags & 0x0400) { copy = state->length; if (copy > have) copy = have; if (copy) { if (state->head != Z_NULL && state->head->extra != Z_NULL) { len = state->head->extra_len - state->length; zmemcpy(state->head->extra + len, next, len + copy > state->head->extra_max ? state->head->extra_max - len : copy); } if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; state->length -= copy; } if (state->length) goto inf_leave; } state->length = 0; state->mode = NAME; case NAME: if (state->flags & 0x0800) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->name != Z_NULL && state->length < state->head->name_max) state->head->name[state->length++] = (Bytef)len; } while (len && copy < have); if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->name = Z_NULL; state->length = 0; state->mode = COMMENT; case COMMENT: if (state->flags & 0x1000) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->comment != Z_NULL && state->length < state->head->comm_max) state->head->comment[state->length++] = (Bytef)len; } while (len && copy < have); if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->comment = Z_NULL; state->mode = HCRC; case HCRC: if (state->flags & 0x0200) { NEEDBITS(16); if ((state->wrap & 4) && hold != (state->check & 0xffff)) { strm->msg = (char *)"header crc mismatch"; state->mode = BAD; break; } INITBITS(); } if (state->head != Z_NULL) { state->head->hcrc = (int)((state->flags >> 9) & 1); state->head->done = 1; } strm->adler = state->check = crc32(0L, Z_NULL, 0); state->mode = TYPE; break; #endif case DICTID: NEEDBITS(32); strm->adler = state->check = ZSWAP32(hold); INITBITS(); state->mode = DICT; case DICT: if (state->havedict == 0) { RESTORE(); return Z_NEED_DICT; } strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = TYPE; case TYPE: if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; case TYPEDO: if (state->last) { BYTEBITS(); state->mode = CHECK; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", state->last ? " (last)" : "")); state->mode = STORED; break; case 1: /* fixed block */ fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); state->mode = LEN_; /* decode codes */ if (flush == Z_TREES) { DROPBITS(2); goto inf_leave; } break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", state->last ? " (last)" : "")); state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); state->mode = COPY_; if (flush == Z_TREES) goto inf_leave; case COPY_: state->mode = COPY; case COPY: copy = state->length; if (copy) { if (copy > have) copy = have; if (copy > left) copy = left; if (copy == 0) goto inf_leave; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; break; } Tracev((stderr, "inflate: stored end\n")); state->mode = TYPE; break; case TABLE: NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif Tracev((stderr, "inflate: table sizes ok\n")); state->have = 0; state->mode = LENLENS; case LENLENS: while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (const code FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); state->have = 0; state->mode = CODELENS; case CODELENS: while (state->have < state->nlen + state->ndist) { for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = state->lens[state->have - 1]; copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; state->mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (const code FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (const code FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); state->mode = LEN_; if (flush == Z_TREES) goto inf_leave; case LEN_: state->mode = LEN; case LEN: if (have >= 6 && left >= 258) { RESTORE(); inflate_fast(strm, out); LOAD(); if (state->mode == TYPE) state->back = -1; break; } state->back = 0; for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; state->length = (unsigned)here.val; if ((int)(here.op) == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); state->mode = LIT; break; } if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); state->back = -1; state->mode = TYPE; break; } if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } state->extra = (unsigned)(here.op) & 15; state->mode = LENEXT; case LENEXT: if (state->extra) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } Tracevv((stderr, "inflate: length %u\n", state->length)); state->was = state->length; state->mode = DIST; case DIST: for (;;) { here = state->distcode[BITS(state->distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)here.val; state->extra = (unsigned)(here.op) & 15; state->mode = DISTEXT; case DISTEXT: if (state->extra) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } #ifdef INFLATE_STRICT if (state->offset > state->dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif Tracevv((stderr, "inflate: distance %u\n", state->offset)); state->mode = MATCH; case MATCH: if (left == 0) goto inf_leave; copy = out - left; if (state->offset > copy) { /* copy from window */ copy = state->offset - copy; if (copy > state->whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR Trace((stderr, "inflate.c too far\n")); copy -= state->whave; if (copy > state->length) copy = state->length; if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = 0; } while (--copy); if (state->length == 0) state->mode = LEN; break; #endif } if (copy > state->wnext) { copy -= state->wnext; from = state->window + (state->wsize - copy); } else from = state->window + (state->wnext - copy); if (copy > state->length) copy = state->length; } else { /* copy from output */ from = put - state->offset; copy = state->length; } if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = *from++; } while (--copy); if (state->length == 0) state->mode = LEN; break; case LIT: if (left == 0) goto inf_leave; *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; case CHECK: if (state->wrap) { NEEDBITS(32); out -= left; strm->total_out += out; state->total += out; if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE(state->check, put - out, out); out = left; if ((state->wrap & 4) && ( #ifdef GUNZIP state->flags ? hold : #endif ZSWAP32(hold)) != state->check) { strm->msg = (char *)"incorrect data check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: check matches trailer\n")); } #ifdef GUNZIP state->mode = LENGTH; case LENGTH: if (state->wrap && state->flags) { NEEDBITS(32); if (hold != (state->total & 0xffffffffUL)) { strm->msg = (char *)"incorrect length check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: length matches trailer\n")); } #endif state->mode = DONE; case DONE: ret = Z_STREAM_END; goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; case MEM: return Z_MEM_ERROR; case SYNC: default: return Z_STREAM_ERROR; } /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call updatewindow() to create and/or update the window state. Note: a memory error from inflate() is non-recoverable. */ inf_leave: RESTORE(); if (state->wsize || (out != strm->avail_out && state->mode < BAD && (state->mode < CHECK || flush != Z_FINISH))) if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { state->mode = MEM; return Z_MEM_ERROR; } in -= strm->avail_in; out -= strm->avail_out; strm->total_in += in; strm->total_out += out; state->total += out; if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); strm->data_type = (int)state->bits + (state->last ? 64 : 0) + (state->mode == TYPE ? 128 : 0) + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) ret = Z_BUF_ERROR; return ret; } int ZEXPORT inflateEnd(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->window != Z_NULL) ZFREE(strm, state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) z_streamp strm; Bytef *dictionary; uInt *dictLength; { struct inflate_state FAR *state; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* copy dictionary */ if (state->whave && dictionary != Z_NULL) { zmemcpy(dictionary, state->window + state->wnext, state->whave - state->wnext); zmemcpy(dictionary + state->whave - state->wnext, state->window, state->wnext); } if (dictLength != Z_NULL) *dictLength = state->whave; return Z_OK; } int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { struct inflate_state FAR *state; unsigned long dictid; int ret; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; /* check for correct dictionary identifier */ if (state->mode == DICT) { dictid = adler32(0L, Z_NULL, 0); dictid = adler32(dictid, dictionary, dictLength); if (dictid != state->check) return Z_DATA_ERROR; } /* copy dictionary to window using updatewindow(), which will amend the existing dictionary if appropriate */ ret = updatewindow(strm, dictionary + dictLength, dictLength); if (ret) { state->mode = MEM; return Z_MEM_ERROR; } state->havedict = 1; Tracev((stderr, "inflate: dictionary set\n")); return Z_OK; } int ZEXPORT inflateGetHeader(strm, head) z_streamp strm; gz_headerp head; { struct inflate_state FAR *state; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; /* save header structure */ state->head = head; head->done = 0; return Z_OK; } /* Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found or when out of input. When called, *have is the number of pattern bytes found in order so far, in 0..3. On return *have is updated to the new state. If on return *have equals four, then the pattern was found and the return value is how many bytes were read including the last byte of the pattern. If *have is less than four, then the pattern has not been found yet and the return value is len. In the latter case, syncsearch() can be called again with more data and the *have state. *have is initialized to zero for the first call. */ local unsigned syncsearch(have, buf, len) unsigned FAR *have; const unsigned char FAR *buf; unsigned len; { unsigned got; unsigned next; got = *have; next = 0; while (next < len && got < 4) { if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) got++; else if (buf[next]) got = 0; else got = 4 - got; next++; } *have = got; return next; } int ZEXPORT inflateSync(strm) z_streamp strm; { unsigned len; /* number of bytes to look at or looked at */ unsigned long in, out; /* temporary to save total_in and total_out */ unsigned char buf[4]; /* to restore bit buffer to byte string */ struct inflate_state FAR *state; /* check parameters */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; /* if first time, start search in bit buffer */ if (state->mode != SYNC) { state->mode = SYNC; state->hold <<= state->bits & 7; state->bits -= state->bits & 7; len = 0; while (state->bits >= 8) { buf[len++] = (unsigned char)(state->hold); state->hold >>= 8; state->bits -= 8; } state->have = 0; syncsearch(&(state->have), buf, len); } /* search available input */ len = syncsearch(&(state->have), strm->next_in, strm->avail_in); strm->avail_in -= len; strm->next_in += len; strm->total_in += len; /* return no joy or set up to restart inflate() on a new block */ if (state->have != 4) return Z_DATA_ERROR; in = strm->total_in; out = strm->total_out; inflateReset(strm); strm->total_in = in; strm->total_out = out; state->mode = TYPE; return Z_OK; } /* Returns true if inflate is currently at the end of a block generated by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ int ZEXPORT inflateSyncPoint(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; return state->mode == STORED && state->bits == 0; } int ZEXPORT inflateCopy(dest, source) z_streamp dest; z_streamp source; { struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; unsigned wsize; /* check input */ if (inflateStateCheck(source) || dest == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)source->state; /* allocate space */ copy = (struct inflate_state FAR *) ZALLOC(source, 1, sizeof(struct inflate_state)); if (copy == Z_NULL) return Z_MEM_ERROR; window = Z_NULL; if (state->window != Z_NULL) { window = (unsigned char FAR *) ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); if (window == Z_NULL) { ZFREE(source, copy); return Z_MEM_ERROR; } } /* copy state */ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); copy->strm = dest; if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); copy->distcode = copy->codes + (state->distcode - state->codes); } copy->next = copy->codes + (state->next - state->codes); if (window != Z_NULL) { wsize = 1U << state->wbits; zmemcpy(window, state->window, wsize); } copy->window = window; dest->state = (struct internal_state FAR *)copy; return Z_OK; } int ZEXPORT inflateUndermine(strm, subvert) z_streamp strm; int subvert; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR state->sane = !subvert; return Z_OK; #else (void)subvert; state->sane = 1; return Z_DATA_ERROR; #endif } int ZEXPORT inflateValidate(strm, check) z_streamp strm; int check; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (check) state->wrap |= 4; else state->wrap &= ~4; return Z_OK; } long ZEXPORT inflateMark(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return -(1L << 16); state = (struct inflate_state FAR *)strm->state; return (long)(((unsigned long)((long)state->back)) << 16) + (state->mode == COPY ? state->length : (state->mode == MATCH ? state->was - state->length : 0)); } unsigned long ZEXPORT inflateCodesUsed(strm) z_streamp strm; { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return (unsigned long)-1; state = (struct inflate_state FAR *)strm->state; return (unsigned long)(state->next - state->codes); } ================================================ FILE: deps/CascLib/src/zlib/inflate.h ================================================ /* inflate.h -- internal inflate state definition * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ #ifndef __INFLATE_H__ #define __INFLATE_H__ /* define NO_GZIP when compiling if you want to disable gzip header and trailer decoding by inflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip decoding should be left enabled. */ #ifndef NO_GZIP # define GUNZIP #endif /* Possible inflate modes between inflate() calls */ typedef enum { HEAD = 16180, /* i: waiting for magic header */ FLAGS, /* i: waiting for method and flags (gzip) */ TIME, /* i: waiting for modification time (gzip) */ OS, /* i: waiting for extra flags and operating system (gzip) */ EXLEN, /* i: waiting for extra length (gzip) */ EXTRA, /* i: waiting for extra bytes (gzip) */ NAME, /* i: waiting for end of file name (gzip) */ COMMENT, /* i: waiting for end of comment (gzip) */ HCRC, /* i: waiting for header crc (gzip) */ DICTID, /* i: waiting for dictionary check value */ DICT, /* waiting for inflateSetDictionary() call */ TYPE, /* i: waiting for type bits, including last-flag bit */ TYPEDO, /* i: same, but skip check to exit inflate on new block */ STORED, /* i: waiting for stored size (length and complement) */ COPY_, /* i/o: same as COPY below, but only first time in */ COPY, /* i/o: waiting for input or output to copy stored block */ TABLE, /* i: waiting for dynamic block table lengths */ LENLENS, /* i: waiting for code length code lengths */ CODELENS, /* i: waiting for length/lit and distance code lengths */ LEN_, /* i: same as LEN below, but only first time in */ LEN, /* i: waiting for length/lit/eob code */ LENEXT, /* i: waiting for length extra bits */ DIST, /* i: waiting for distance code */ DISTEXT, /* i: waiting for distance extra bits */ MATCH, /* o: waiting for output space to copy string */ LIT, /* o: waiting for output space to write literal */ CHECK, /* i: waiting for 32-bit check value */ LENGTH, /* i: waiting for 32-bit length (gzip) */ DONE, /* finished check, done -- remain here until reset */ BAD, /* got a data error -- remain here until reset */ MEM, /* got an inflate() memory error -- remain here until reset */ SYNC /* looking for synchronization bytes to restart inflate() */ } inflate_mode; /* State transitions between above modes - (most modes can go to BAD or MEM on error -- not shown for clarity) Process header: HEAD -> (gzip) or (zlib) or (raw) (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> HCRC -> TYPE (zlib) -> DICTID or TYPE DICTID -> DICT -> TYPE (raw) -> TYPEDO Read deflate blocks: TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK STORED -> COPY_ -> COPY -> TYPE TABLE -> LENLENS -> CODELENS -> LEN_ LEN_ -> LEN Read deflate codes in fixed or dynamic block: LEN -> LENEXT or LIT or TYPE LENEXT -> DIST -> DISTEXT -> MATCH -> LEN LIT -> LEN Process trailer: CHECK -> LENGTH -> DONE */ /* State maintained between inflate() calls -- approximately 7K bytes, not including the allocated sliding window, which is up to 32K bytes. */ struct inflate_state { z_streamp strm; /* pointer back to this zlib stream */ inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip, bit 2 true to validate check value */ int havedict; /* true if dictionary provided */ int flags; /* gzip header method and flags (0 if zlib) */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ unsigned long check; /* protected copy of check value */ unsigned long total; /* protected copy of output count */ gz_headerp head; /* where to save gzip header information */ /* sliding window */ unsigned wbits; /* log base 2 of requested window size */ unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if needed */ /* bit accumulator */ unsigned long hold; /* input bit accumulator */ unsigned bits; /* number of bits in "in" */ /* for string and stored block copying */ unsigned length; /* literal or length of data to copy */ unsigned offset; /* distance back to copy string from */ /* for table and code decoding */ unsigned extra; /* extra bits needed */ /* fixed and dynamic code tables */ code const FAR *lencode; /* starting table for length/literal codes */ code const FAR *distcode; /* starting table for distance codes */ unsigned lenbits; /* index bits for lencode */ unsigned distbits; /* index bits for distcode */ /* dynamic table building */ unsigned ncode; /* number of code length code lengths */ unsigned nlen; /* number of length code lengths */ unsigned ndist; /* number of distance code lengths */ unsigned have; /* number of code lengths in lens[] */ code FAR *next; /* next available space in codes[] */ unsigned short lens[320]; /* temporary storage for code lengths */ unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ int sane; /* if false, allow invalid distance too far */ int back; /* bits back of last unprocessed length/lit */ unsigned was; /* initial length of match */ }; #endif // __INFLATE_H__ ================================================ FILE: deps/CascLib/src/zlib/inftrees.c ================================================ /* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #define MAXBITS 15 const char inflate_copyright[] = " inflate 1.2.11 Copyright 1995-2017 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* Build a set of tables to decode the provided canonical Huffman code. The code lengths are lens[0..codes-1]. The result starts at *table, whose indices are 0..2^bits-1. work is a writable array of at least lens shorts, which is used as a work area. type is the type of code to be generated, CODES, LENS, or DISTS. On return, zero is success, -1 is an invalid code, and +1 means that ENOUGH isn't enough. table on return points to the next available entry's address. bits is the requested root table index bits, and on return it is the actual root table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) codetype type; unsigned short FAR *lens; unsigned codes; code FAR * FAR *table; unsigned FAR *bits; unsigned short FAR *work; { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ unsigned root; /* number of index bits for root table */ unsigned curr; /* number of index bits for current table */ unsigned drop; /* code bits to drop for sub-table */ int left; /* number of prefix codes available */ unsigned used; /* code entries in table used */ unsigned huff; /* Huffman code */ unsigned incr; /* for incrementing code, index */ unsigned fill; /* index for replicating entries */ unsigned low; /* low bits for current root entry */ unsigned mask; /* mask for low root bits */ code here; /* table entry for duplication */ code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ unsigned match; /* use base and extra for symbol >= match */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64}; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) count[len] = 0; for (sym = 0; sym < codes; sym++) count[lens[sym]]++; /* bound code lengths, force root to be within code lengths */ root = *bits; for (max = MAXBITS; max >= 1; max--) if (count[max] != 0) break; if (root > max) root = max; if (max == 0) { /* no symbols to code at all */ here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)1; here.val = (unsigned short)0; *(*table)++ = here; /* make a table to force an error */ *(*table)++ = here; *bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < max; min++) if (count[min] != 0) break; if (root < min) root = min; /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) return -1; /* over-subscribed */ } if (left > 0 && (type == CODES || max != 1)) return -1; /* incomplete set */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + count[len]; /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftrees.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ switch (type) { case CODES: base = extra = work; /* dummy value--not used */ match = 20; break; case LENS: base = lbase; extra = lext; match = 257; break; default: /* DISTS */ base = dbase; extra = dext; match = 0; } /* initialize state for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = *table; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = (unsigned)(-1); /* trigger new sub-table when len > root */ used = 1U << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type == LENS && used > ENOUGH_LENS) || (type == DISTS && used > ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ for (;;) { /* create table entry */ here.bits = (unsigned char)(len - drop); if (work[sym] + 1U < match) { here.op = (unsigned char)0; here.val = work[sym]; } else if (work[sym] >= match) { here.op = (unsigned char)(extra[work[sym] - match]); here.val = base[work[sym] - match]; } else { here.op = (unsigned char)(32 + 64); /* end of block */ here.val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1U << (len - drop); fill = 1U << curr; min = fill; /* save offset to next table */ do { fill -= incr; next[(huff >> drop) + fill] = here; } while (fill != 0); /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; /* go to next symbol, update count, len */ sym++; if (--(count[len]) == 0) { if (len == max) break; len = lens[work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) != low) { /* if first time, transition to sub-tables */ if (drop == 0) drop = root; /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = (int)(1 << curr); while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) break; curr++; left <<= 1; } /* check for enough space */ used += 1U << curr; if ((type == LENS && used > ENOUGH_LENS) || (type == DISTS && used > ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ low = huff & mask; (*table)[low].op = (unsigned char)curr; (*table)[low].bits = (unsigned char)root; (*table)[low].val = (unsigned short)(next - *table); } } /* fill in remaining table entry if code is incomplete (guaranteed to have at most one remaining entry, since if the code is incomplete, the maximum code length that was allowed to get this far is one bit) */ if (huff != 0) { here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)(len - drop); here.val = (unsigned short)0; next[huff] = here; } /* set return parameters */ *table += used; *bits = root; return 0; } ================================================ FILE: deps/CascLib/src/zlib/inftrees.h ================================================ /* inftrees.h -- header to use inftrees.c * Copyright (C) 1995-2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ #ifndef __INFTREES_H_ #define __INFTREES_H_ /* Structure for decoding tables. Each entry provides either the information needed to do the operation requested by the code that indexed that table entry, or it provides a pointer to another table that indexes more bits of the code. op indicates whether the entry is a pointer to another table, a literal, a length or distance, an end-of-block, or an invalid code. For a table pointer, the low four bits of op is the number of index bits of that table. For a length or distance, the low four bits of op is the number of extra bits to get after the code. bits is the number of bits in this code or part of the code to drop off of the bit buffer. val is the actual byte to output in the case of a literal, the base length or distance, or the offset from the current table to the next table. Each entry is four bytes. */ typedef struct { unsigned char op; /* operation, extra bits, table bits */ unsigned char bits; /* bits in this part of the code */ unsigned short val; /* offset in table or code value */ } code; /* op values as set by inflate_table(): 00000000 - literal 0000tttt - table link, tttt != 0 is the number of table index bits 0001eeee - length or distance, eeee is the number of extra bits 01100000 - end of block 01000000 - invalid code */ /* Maximum size of the dynamic table. The maximum number of code structures is 1444, which is the sum of 852 for literal/length codes and 592 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribtution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns returns 852, and "enough 30 6 15" for distance codes returns 592. The initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ #define ENOUGH_LENS 852 #define ENOUGH_DISTS 592 #define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) /* Type of code to build for inflate_table() */ typedef enum { CODES, LENS, DISTS } codetype; int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work)); #endif // __INFTREES_H_ ================================================ FILE: deps/CascLib/src/zlib/trees.c ================================================ /* trees.c -- output deflated data using Huffman coding * Copyright (C) 1995-2017 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process uses several Huffman trees. The more * common source values are represented by shorter bit sequences. * * Each code tree is stored in a compressed form which is itself * a Huffman encoding of the lengths of all the code strings (in * ascending order by source values). The actual code strings are * reconstructed from the lengths in the inflate process, as described * in the deflate specification. * * REFERENCES * * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc * * Storer, James A. * Data Compression: Methods and Theory, pp. 49-50. * Computer Science Press, 1988. ISBN 0-7167-8156-5. * * Sedgewick, R. * Algorithms, p290. * Addison-Wesley, 1983. ISBN 0-201-06672-6. */ /* @(#) $Id$ */ /* #define GEN_TREES_H */ #include "deflate.h" #ifdef ZLIB_DEBUG # include #endif /* =========================================================================== * Constants */ #define MAX_BL_BITS 7 /* Bit length codes must not exceed MAX_BL_BITS bits */ #define END_BLOCK 256 /* end of block literal code */ #define REP_3_6 16 /* repeat previous bit length 3-6 times (2 bits of repeat count) */ #define REPZ_3_10 17 /* repeat a zero length 3-10 times (3 bits of repeat count) */ #define REPZ_11_138 18 /* repeat a zero length 11-138 times (7 bits of repeat count) */ local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; local const int extra_dbits[D_CODES] /* extra bits for each distance code */ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; local const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ /* =========================================================================== * Local data. These are initialized only once. */ #define DIST_CODE_LEN 512 /* see definition of array dist_code below */ #if defined(GEN_TREES_H) || !defined(STDC) /* non ANSI compilers may not accept trees.h */ local ct_data static_ltree[L_CODES+2]; /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ local ct_data static_dtree[D_CODES]; /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ uch _dist_code[DIST_CODE_LEN]; /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ uch _length_code[MAX_MATCH-MIN_MATCH+1]; /* length code for each normalized match length (0 == MIN_MATCH) */ local int base_length[LENGTH_CODES]; /* First normalized length for each code (0 = MIN_MATCH) */ local int base_dist[D_CODES]; /* First normalized distance for each code (0 = distance of 1) */ #else # include "trees.h" #endif /* GEN_TREES_H */ struct static_tree_desc_s { const ct_data *static_tree; /* static tree or NULL */ const intf *extra_bits; /* extra bits for each code or NULL */ int extra_base; /* base index for extra_bits */ int elems; /* max number of elements in the tree */ int max_length; /* max bit length for the codes */ }; local const static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; local const static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; local const static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== * Local (static) routines in this file. */ local void tr_static_init OF((void)); local void init_block OF((deflate_state *s)); local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); local void build_tree OF((deflate_state *s, tree_desc *desc)); local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); local int build_bl_tree OF((deflate_state *s)); local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, int blcodes)); local void compress_block OF((deflate_state *s, const ct_data *ltree, const ct_data *dtree)); local int detect_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); #ifdef GEN_TREES_H local void gen_trees_header OF((void)); #endif #ifndef ZLIB_DEBUG # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ #else /* !ZLIB_DEBUG */ # define send_code(s, c, tree) \ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } #endif /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ #define put_short(s, w) { \ put_byte(s, (uch)((w) & 0xff)); \ put_byte(s, (uch)((ush)(w) >> 8)); \ } /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef ZLIB_DEBUG local void send_bits OF((deflate_state *s, int value, int length)); local void send_bits(s, value, length) deflate_state *s; int value; /* value to send */ int length; /* number of bits */ { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; /* If not enough room in bi_buf, use (valid) bits from bi_buf and * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { s->bi_buf |= (ush)value << s->bi_valid; put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { s->bi_buf |= (ush)value << s->bi_valid; s->bi_valid += length; } } #else /* !ZLIB_DEBUG */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = (int)value;\ s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ s->bi_buf |= (ush)(value) << s->bi_valid;\ s->bi_valid += len;\ }\ } #endif /* ZLIB_DEBUG */ /* the arguments must not have side effects */ /* =========================================================================== * Initialize the various 'constant' tables. */ local void tr_static_init() { #if defined(GEN_TREES_H) || !defined(STDC) static int static_init_done = 0; int n; /* iterates over tree elements */ int bits; /* bit counter */ int length; /* length value */ int code; /* code value */ int dist; /* distance index */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ if (static_init_done) return; /* For some embedded targets, global variables are not initialized: */ #ifdef NO_INIT_GLOBAL_POINTERS static_l_desc.static_tree = static_ltree; static_l_desc.extra_bits = extra_lbits; static_d_desc.static_tree = static_dtree; static_d_desc.extra_bits = extra_dbits; static_bl_desc.extra_bits = extra_blbits; #endif /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; for (n = 0; n < (1< dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ for ( ; code < D_CODES; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { _dist_code[256 + dist++] = (uch)code; } } Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; n = 0; while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES; n++) { static_dtree[n].Len = 5; static_dtree[n].Code = bi_reverse((unsigned)n, 5); } static_init_done = 1; # ifdef GEN_TREES_H gen_trees_header(); # endif #endif /* defined(GEN_TREES_H) || !defined(STDC) */ } /* =========================================================================== * Genererate the file trees.h describing the static trees. */ #ifdef GEN_TREES_H # ifndef ZLIB_DEBUG # include # endif # define SEPARATOR(i, last, width) \ ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width)-1 ? ",\n" : ", ")) void gen_trees_header() { FILE *header = fopen("trees.h", "w"); int i; Assert (header != NULL, "Can't open trees.h"); fprintf(header, "/* header created automatically with -DGEN_TREES_H */\n\n"); fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); for (i = 0; i < L_CODES+2; i++) { fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); } fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); } fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); for (i = 0; i < DIST_CODE_LEN; i++) { fprintf(header, "%2u%s", _dist_code[i], SEPARATOR(i, DIST_CODE_LEN-1, 20)); } fprintf(header, "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { fprintf(header, "%2u%s", _length_code[i], SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); } fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); for (i = 0; i < LENGTH_CODES; i++) { fprintf(header, "%1u%s", base_length[i], SEPARATOR(i, LENGTH_CODES-1, 20)); } fprintf(header, "local const int base_dist[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "%5u%s", base_dist[i], SEPARATOR(i, D_CODES-1, 10)); } fclose(header); } #endif /* GEN_TREES_H */ /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ void ZLIB_INTERNAL _tr_init(s) deflate_state *s; { tr_static_init(); s->l_desc.dyn_tree = s->dyn_ltree; s->l_desc.stat_desc = &static_l_desc; s->d_desc.dyn_tree = s->dyn_dtree; s->d_desc.stat_desc = &static_d_desc; s->bl_desc.dyn_tree = s->bl_tree; s->bl_desc.stat_desc = &static_bl_desc; s->bi_buf = 0; s->bi_valid = 0; #ifdef ZLIB_DEBUG s->compressed_len = 0L; s->bits_sent = 0L; #endif /* Initialize the first block of the first file: */ init_block(s); } /* =========================================================================== * Initialize a new block. */ local void init_block(s) deflate_state *s; { int n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; s->dyn_ltree[END_BLOCK].Freq = 1; s->opt_len = s->static_len = 0L; s->last_lit = s->matches = 0; } #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ /* =========================================================================== * Remove the smallest element from the heap and recreate the heap with * one less element. Updates heap and heap_len. */ #define pqremove(s, tree, top) \ {\ top = s->heap[SMALLEST]; \ s->heap[SMALLEST] = s->heap[s->heap_len--]; \ pqdownheap(s, tree, SMALLEST); \ } /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ #define smaller(tree, n, m, depth) \ (tree[n].Freq < tree[m].Freq || \ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ local void pqdownheap(s, tree, k) deflate_state *s; ct_data *tree; /* the tree to restore */ int k; /* node to move down */ { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { /* Set j to the smallest of the two sons: */ if (j < s->heap_len && smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s->heap[j], s->depth)) break; /* Exchange v with the smallest son */ s->heap[k] = s->heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s->heap[k] = v; } /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ local void gen_bitlen(s, desc) deflate_state *s; tree_desc *desc; /* the tree descriptor */ { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; const ct_data *stree = desc->stat_desc->static_tree; const intf *extra = desc->stat_desc->extra_bits; int base = desc->stat_desc->extra_base; int max_length = desc->stat_desc->max_length; int h; /* heap index */ int n, m; /* iterate over the tree elements */ int bits; /* bit length */ int xbits; /* extra bits */ ush f; /* frequency */ int overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ for (h = s->heap_max+1; h < HEAP_SIZE; h++) { n = s->heap[h]; bits = tree[tree[n].Dad].Len + 1; if (bits > max_length) bits = max_length, overflow++; tree[n].Len = (ush)bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) continue; /* not a leaf node */ s->bl_count[bits]++; xbits = 0; if (n >= base) xbits = extra[n-base]; f = tree[n].Freq; s->opt_len += (ulg)f * (unsigned)(bits + xbits); if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits); } if (overflow == 0) return; Tracev((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length-1; while (s->bl_count[bits] == 0) bits--; s->bl_count[bits]--; /* move one leaf down the tree */ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ s->bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from 'ar' written by Haruhiko Okumura.) */ for (bits = max_length; bits != 0; bits--) { n = s->bl_count[bits]; while (n != 0) { m = s->heap[--h]; if (m > max_code) continue; if ((unsigned) tree[m].Len != (unsigned) bits) { Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq; tree[m].Len = (ush)bits; } n--; } } } /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ local void gen_codes (tree, max_code, bl_count) ct_data *tree; /* the tree to decorate */ int max_code; /* largest code with non zero frequency */ ushf *bl_count; /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ unsigned code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { code = (code + bl_count[bits-1]) << 1; next_code[bits] = (ush)code; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; const ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; int n, m; /* iterate over heap elements */ int max_code = -1; /* largest code with non zero frequency */ int node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s->heap_len = 0, s->heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { if (tree[n].Freq != 0) { s->heap[++(s->heap_len)] = max_code = n; s->depth[n] = 0; } else { tree[n].Len = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s->heap_len < 2) { node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); tree[node].Freq = 1; s->depth[node] = 0; s->opt_len--; if (stree) s->static_len -= stree[node].Len; /* node is 0 or 1 so it does not have extra bits */ } desc->max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { pqremove(s, tree, n); /* n = node of least frequency */ m = s->heap[SMALLEST]; /* m = node of next least frequency */ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ s->heap[--(s->heap_max)] = m; /* Create a new node father of n and m */ tree[node].Freq = tree[n].Freq + tree[m].Freq; s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? s->depth[n] : s->depth[m]) + 1); tree[n].Dad = tree[m].Dad = (ush)node; #ifdef DUMP_BL_TREE if (tree == s->bl_tree) { fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); } #endif /* and insert the new node in the heap */ s->heap[SMALLEST] = node++; pqdownheap(s, tree, SMALLEST); } while (s->heap_len >= 2); s->heap[--(s->heap_max)] = s->heap[SMALLEST]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, (tree_desc *)desc); /* The field len is now set, we can generate the bit codes */ gen_codes ((ct_data *)tree, max_code, s->bl_count); } /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ local void scan_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ if (nextlen == 0) max_count = 138, min_count = 3; tree[max_code+1].Len = (ush)0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { s->bl_tree[curlen].Freq += count; } else if (curlen != 0) { if (curlen != prevlen) s->bl_tree[curlen].Freq++; s->bl_tree[REP_3_6].Freq++; } else if (count <= 10) { s->bl_tree[REPZ_3_10].Freq++; } else { s->bl_tree[REPZ_11_138].Freq++; } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ local void send_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen == 0) max_count = 138, min_count = 3; for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s->bl_tree); } while (--count != 0); } else if (curlen != 0) { if (curlen != prevlen) { send_code(s, curlen, s->bl_tree); count--; } Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); } else { send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ local int build_bl_tree(s) deflate_state *s; { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); /* Build the bit length tree: */ build_tree(s, (tree_desc *)(&(s->bl_desc))); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); return max_blindex; } /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ local void send_all_trees(s, lcodes, dcodes, blcodes) deflate_state *s; int lcodes, dcodes, blcodes; /* number of codes for each tree */ { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, "too many codes"); Tracev((stderr, "\nbl counts: ")); send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes-1, 5); send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); } Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } /* =========================================================================== * Send a stored block */ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block */ ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ bi_windup(s); /* align on byte boundary */ put_short(s, (ush)stored_len); put_short(s, (ush)~stored_len); zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); s->pending += stored_len; #ifdef ZLIB_DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; s->bits_sent += 2*16; s->bits_sent += stored_len<<3; #endif } /* =========================================================================== * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) */ void ZLIB_INTERNAL _tr_flush_bits(s) deflate_state *s; { bi_flush(s); } /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ void ZLIB_INTERNAL _tr_align(s) deflate_state *s; { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef ZLIB_DEBUG s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and write out the encoded block. */ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block, or NULL if too old */ ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s->level > 0) { /* Check if the file is binary or text */ if (s->strm->data_type == Z_UNKNOWN) s->strm->data_type = detect_data_type(s); /* Construct the literal and distance trees */ build_tree(s, (tree_desc *)(&(s->l_desc))); Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, s->static_len)); build_tree(s, (tree_desc *)(&(s->d_desc))); Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ opt_lenb = (s->opt_len+3+7)>>3; static_lenb = (s->static_len+3+7)>>3; Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->last_lit)); if (static_lenb <= opt_lenb) opt_lenb = static_lenb; } else { Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } #ifdef FORCE_STORED if (buf != (char*)0) { /* force stored block */ #else if (stored_len+4 <= opt_lenb && buf != (char*)0) { /* 4: two words for the lengths */ #endif /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can't have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block(s, buf, stored_len, last); #ifdef FORCE_STATIC } else if (static_lenb >= 0) { /* force static trees */ #else } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { #endif send_bits(s, (STATIC_TREES<<1)+last, 3); compress_block(s, (const ct_data *)static_ltree, (const ct_data *)static_dtree); #ifdef ZLIB_DEBUG s->compressed_len += 3 + s->static_len; #endif } else { send_bits(s, (DYN_TREES<<1)+last, 3); send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, max_blindex+1); compress_block(s, (const ct_data *)s->dyn_ltree, (const ct_data *)s->dyn_dtree); #ifdef ZLIB_DEBUG s->compressed_len += 3 + s->opt_len; #endif } Assert (s->compressed_len == s->bits_sent, "bad compressed size"); /* The above check is made mod 2^32, for files larger than 512 MB * and uLong implemented on 32 bits. */ init_block(s); if (last) { bi_windup(s); #ifdef ZLIB_DEBUG s->compressed_len += 7; /* align on byte boundary */ #endif } Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, s->compressed_len-7*last)); } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ int ZLIB_INTERNAL _tr_tally (s, dist, lc) deflate_state *s; unsigned dist; /* distance of matched string */ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ { s->d_buf[s->last_lit] = (ush)dist; s->l_buf[s->last_lit++] = (uch)lc; if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; } else { s->matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ Assert((ush)dist < (ush)MAX_DIST(s) && (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; s->dyn_dtree[d_code(dist)].Freq++; } #ifdef TRUNCATE_BLOCK /* Try to guess if it is profitable to stop the current block here */ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { /* Compute an upper bound for the compressed length */ ulg out_length = (ulg)s->last_lit*8L; ulg in_length = (ulg)((long)s->strstart - s->block_start); int dcode; for (dcode = 0; dcode < D_CODES; dcode++) { out_length += (ulg)s->dyn_dtree[dcode].Freq * (5L+extra_dbits[dcode]); } out_length >>= 3; Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", s->last_lit, in_length, out_length, 100L - out_length*100L/in_length)); if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; } #endif return (s->last_lit == s->lit_bufsize-1); /* We avoid equality with lit_bufsize because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ } /* =========================================================================== * Send the block data compressed using the given Huffman trees */ local void compress_block(s, ltree, dtree) deflate_state *s; const ct_data *ltree; /* literal tree */ const ct_data *dtree; /* distance tree */ { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ unsigned lx = 0; /* running index in l_buf */ unsigned code; /* the code to send */ int extra; /* number of extra bits to send */ if (s->last_lit != 0) do { dist = s->d_buf[lx]; lc = s->l_buf[lx++]; if (dist == 0) { send_code(s, lc, ltree); /* send a literal byte */ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; send_code(s, code+LITERALS+1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra != 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { dist -= (unsigned)base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, "pendingBuf overflow"); } while (lx < s->last_lit); send_code(s, END_BLOCK, ltree); } /* =========================================================================== * Check if the data type is TEXT or BINARY, using the following algorithm: * - TEXT if the two conditions below are satisfied: * a) There are no non-portable control characters belonging to the * "black list" (0..6, 14..25, 28..31). * b) There is at least one printable character belonging to the * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). * - BINARY otherwise. * - The following partially-portable control characters form a * "gray list" that is ignored in this detection algorithm: * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ local int detect_data_type(s) deflate_state *s; { /* black_mask is the bit mask of black-listed bytes * set bits 0..6, 14..25, and 28..31 * 0xf3ffc07f = binary 11110011111111111100000001111111 */ unsigned long black_mask = 0xf3ffc07fUL; int n; /* Check for non-textual ("black-listed") bytes. */ for (n = 0; n <= 31; n++, black_mask >>= 1) if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) return Z_BINARY; /* Check for textual ("white-listed") bytes. */ if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 || s->dyn_ltree[13].Freq != 0) return Z_TEXT; for (n = 32; n < LITERALS; n++) if (s->dyn_ltree[n].Freq != 0) return Z_TEXT; /* There are no "black-listed" or "white-listed" bytes: * this stream either is empty or has tolerated ("gray-listed") bytes only. */ return Z_BINARY; } /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ local unsigned bi_reverse(code, len) unsigned code; /* the value to invert */ int len; /* its bit length */ { register unsigned res = 0; do { res |= code & 1; code >>= 1, res <<= 1; } while (--len > 0); return res >> 1; } /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ local void bi_flush(s) deflate_state *s; { if (s->bi_valid == 16) { put_short(s, s->bi_buf); s->bi_buf = 0; s->bi_valid = 0; } else if (s->bi_valid >= 8) { put_byte(s, (Byte)s->bi_buf); s->bi_buf >>= 8; s->bi_valid -= 8; } } /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ local void bi_windup(s) deflate_state *s; { if (s->bi_valid > 8) { put_short(s, s->bi_buf); } else if (s->bi_valid > 0) { put_byte(s, (Byte)s->bi_buf); } s->bi_buf = 0; s->bi_valid = 0; #ifdef ZLIB_DEBUG s->bits_sent = (s->bits_sent+7) & ~7; #endif } ================================================ FILE: deps/CascLib/src/zlib/trees.h ================================================ /* header created automatically with -DGEN_TREES_H */ local const ct_data static_ltree[L_CODES+2] = { {{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, {{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, {{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, {{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, {{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, {{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, {{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, {{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, {{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, {{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, {{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, {{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, {{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, {{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, {{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, {{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, {{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, {{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, {{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, {{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, {{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, {{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, {{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, {{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, {{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, {{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, {{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, {{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, {{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, {{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, {{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, {{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, {{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, {{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, {{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, {{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, {{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, {{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, {{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, {{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, {{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, {{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, {{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, {{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, {{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, {{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, {{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, {{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, {{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, {{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, {{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, {{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, {{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, {{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, {{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, {{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, {{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, {{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} }; local const ct_data static_dtree[D_CODES] = { {{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, {{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, {{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, {{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, {{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, {{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} }; const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 }; local const int base_length[LENGTH_CODES] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 }; local const int base_dist[D_CODES] = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 }; ================================================ FILE: deps/CascLib/src/zlib/zconf.h ================================================ /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET /* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_bits z__tr_flush_bits # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateGetDictionary z_deflateGetDictionary # define deflateInit z_deflateInit # define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateResetKeep z_deflateResetKeep # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzfread z_gzfread # define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # ifdef _WIN32 # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ # define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader # define inflateInit z_inflateInit # define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress # define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO # define zcalloc z_zcalloc # define zcfree z_zcfree # endif # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # ifndef Z_SOLO # define gzFile z_gzFile # endif # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif #if defined(ZLIB_CONST) && !defined(z_const) # define z_const const #else # define z_const #endif #ifdef Z_SOLO typedef unsigned long z_size_t; #else # define z_longlong long long # if defined(NO_SIZE_T) typedef unsigned NO_SIZE_T z_size_t; # elif defined(STDC) # include typedef size_t z_size_t; # else typedef unsigned long z_size_t; # endif # undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif #ifndef Z_ARG /* function prototypes for stdarg */ # if defined(STDC) || defined(Z_HAVE_STDARG_H) # define Z_ARG(args) args # else # define Z_ARG(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT # if defined(M_I86SM) || defined(M_I86MM) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR _far # else # define FAR far # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far # else # define FAR far # endif # endif #endif #if defined(WINDOWS) || defined(WIN32) /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ # ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) # else # define ZEXTERN extern __declspec(dllimport) # endif # endif # endif /* ZLIB_DLL */ /* If building or using zlib with the WINAPI/WINAPIV calling convention, * define ZLIB_WINAPI. * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ # ifdef ZLIB_WINAPI # ifdef FAR # undef FAR # endif # include /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ # define ZEXPORT WINAPI # ifdef WIN32 # define ZEXPORTVA WINAPIV # else # define ZEXPORTVA FAR CDECL # endif # endif #endif #if defined (__BEOS__) # ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) # else # define ZEXPORT __declspec(dllimport) # define ZEXPORTVA __declspec(dllimport) # endif # endif #endif #ifndef ZEXTERN # define ZEXTERN extern #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) # include # if (UINT_MAX == 0xffffffffUL) # define Z_U4 unsigned # elif (ULONG_MAX == 0xffffffffUL) # define Z_U4 unsigned long # elif (USHRT_MAX == 0xffffffffUL) # define Z_U4 unsigned short # endif #endif #ifdef Z_U4 typedef Z_U4 z_crc_t; #else typedef unsigned long z_crc_t; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H #endif #ifdef STDC # ifndef Z_SOLO # include /* for off_t */ # endif #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO # include /* for va_list */ # endif #endif #ifdef _WIN32 # ifndef Z_SOLO # include /* for wchar_t */ # endif #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) # define Z_HAVE_UNISTD_H #endif #ifndef Z_SOLO # if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) # include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif # endif #endif #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 # define Z_LFS64 #endif #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) # define Z_LARGE64 #endif #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) # define Z_WANT64 #endif #if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ ================================================ FILE: deps/CascLib/src/zlib/zlib.h ================================================ /* zlib.h -- interface of the 'zlib' general purpose compression library version 1.2.11, January 15th, 2017 Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ #ifndef ZLIB_H #define ZLIB_H #include "zconf.h" #ifdef __cplusplus extern "C" { #endif #define ZLIB_VERSION "1.2.11" #define ZLIB_VERNUM 0x12b0 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 #define ZLIB_VER_REVISION 11 #define ZLIB_VER_SUBREVISION 0 /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms will be added later and will have the same stream interface. Compression can be done in a single step if the buffers are large enough, or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. This library can optionally read and write gzip and raw deflate streams in memory as well. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in the case of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address)); struct internal_state; typedef struct z_stream_s { z_const Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total number of input bytes read so far */ Bytef *next_out; /* next output byte will go here */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total number of bytes output so far */ z_const char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: binary or text for deflate, or the decoding state for inflate */ uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream FAR *z_streamp; /* gzip header information passed to and from zlib routines. See RFC 1952 for more details on the meanings of these fields. */ typedef struct gz_header_s { int text; /* true if compressed data believed to be text */ uLong time; /* modification time */ int xflags; /* extra flags (not used when writing a gzip file) */ int os; /* operating system */ Bytef *extra; /* pointer to extra field or Z_NULL if none */ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ uInt extra_max; /* space at extra (only when reading header) */ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ uInt name_max; /* space at name (only when reading header) */ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ uInt comm_max; /* space at comment (only when reading header) */ int hcrc; /* true if there was or will be a header crc */ int done; /* true when done reading gzip header (not used when writing a gzip file) */ } gz_header; typedef gz_header FAR *gz_headerp; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. In that case, zlib is thread-safe. When zalloc and zfree are Z_NULL on entry to the initialization function, they are set to internal routines that use the standard library functions malloc() and free(). On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use by the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 #define Z_TREES 6 /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_RLE 3 #define Z_FIXED 4 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_TEXT 1 #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_UNKNOWN 2 /* Possible values of the data_type field for deflate() */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ /* basic functions */ ZEXTERN const char * ZEXPORT zlibVersion OF((void)); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check is automatically made by deflateInit and inflateInit. */ /* ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Generate more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary. Some output may be provided even if flush is zero. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. See deflatePending(), which can be used if desired to determine whether or not there is more ouput in that case. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression algorithms and so it should be used only when necessary. This completes the current deflate block and follows it with an empty stored block that is three bits plus filler bits to the next byte, followed by four bytes (00 00 ff ff). If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the output buffer, but the output is not aligned to a byte boundary. All of the input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. This completes the current deflate block and follows it with an empty fixed codes block that is 10 bits long. This assures that enough bytes are output in order for the decompressor to finish the block before the empty fixed codes block. If flush is set to Z_BLOCK, a deflate block is completed and emitted, as for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to seven bits of the current block are held to be written as the next byte after the next deflate block is completed. In this case, the decompressor may not be provided enough bits at this point in order to complete decompression of the data provided so far to the compressor. It may need to wait for the next block to be emitted. This is for advanced applications that need to control the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using Z_FULL_FLUSH too often can seriously degrade compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used in the first deflate call after deflateInit if all the compression is to be done in a single step. In order to complete in one call, avail_out must be at least the value returned by deflateBound (see below). Then deflate is guaranteed to return Z_STREAM_END. If not enough output space is provided, deflate will not return Z_STREAM_END, and it must be called again as described above. deflate() sets strm->adler to the Adler-32 checksum of all input read so far (that is, total_in bytes). If a gzip stream is being generated, then strm->adler will be the CRC-32 checksum of the input read so far. (See deflateInit2 below.) deflate() may update strm->data_type if it can make a good guess about the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was Z_NULL or the state was inadvertently written over by the application), or Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and deflate() can be called again with more input and more output space to continue compressing. */ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. In the current version of inflate, the provided input is not read or consumed. The allocation of a sliding window will be deferred to the first call of inflate (if the decompression does not complete on the first call). If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit does not perform any decompression. Actual decompression will be done by inflate(). So next_in, and avail_in, next_out, and avail_out are unused and unchanged. The current implementation of inflateInit() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), then next_in and avail_in are updated accordingly, and processing will resume at this point for the next call of inflate(). - Generate more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. If the caller of inflate() does not provide both available input and available output space, it is possible that there will be no progress made. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much output as possible to the output buffer. Z_BLOCK requests that inflate() stop if and when it gets to the next deflate block boundary. When decoding the zlib or gzip format, this will cause inflate() to return immediately after the header and before the first block. When doing a raw inflate, inflate() will go ahead and process the first block, and will return when it gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. To assist in this, on return inflate() always sets strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or decoding the complete header up to just before the first byte of the deflate stream. The end-of-block will not be indicated until all of the uncompressed data from that block has been written to strm->next_out. The number of unused bits may in general be greater than seven, except when bit 7 of data_type is set, in which case the number of unused bits will be less than eight. data_type is set as noted here every time inflate() returns for all flush options, and so can be used to determine the amount of currently consumed input in bits. The Z_TREES option behaves as Z_BLOCK does, but it also returns when the end of each deflate block header is reached, before any actual data in that block is decoded. This allows the caller to determine the length of the deflate block header for later use in random access within a deflate block. 256 is added to the value of strm->data_type when inflate() returns immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all of the uncompressed data for the operation to complete. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The use of Z_FINISH is not required to perform an inflation in one step. However it may be used to inform inflate that a faster approach can be used for the single inflate() call. Z_FINISH also informs inflate to not maintain a sliding window if the stream completes, which reduces inflate's memory footprint. If the stream does not complete, either because not all of the stream is provided or not enough output space is provided, then a sliding window will be allocated and inflate() can be called again to continue the operation as if Z_NO_FLUSH had been used. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the first call. So the effects of the flush parameter in this implementation are on the return value of inflate() as noted below, when inflate() returns early when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of memory for a sliding window when Z_FINISH is used. If a preset dictionary is needed after this call (see inflateSetDictionary below), inflate sets strm->adler to the Adler-32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed Adler-32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() can decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip header is not retained unless inflateGetHeader() is used. When processing gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output produced so far. The CRC-32 is checked against the gzip trailer, as is the uncompressed length, modulo 2^32. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value, in which case strm->msg points to a string with a more specific error), Z_STREAM_ERROR if the stream structure was inconsistent (for example next_in or next_out was Z_NULL, or the state was inadvertently written over by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress was possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial recovery of the data is to be attempted. */ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state was inconsistent. */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)); This is another version of deflateInit with more compression options. The fields next_in, zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. For the current implementation of deflate(), a windowBits value of 8 (a window size of 256 bytes) is not supported. As a result, a request for 8 will result in 9 (a 512-byte window). In that case, providing 8 to inflateInit2() will result in an error when the zlib header with 9 is checked against the initialization of inflate(). The remedy is to not use 8 with deflateInit2() with this initialization, or at least in that case use 9 with inflateInit2(). windowBits can also be -8..-15 for raw deflate. In this case, -windowBits determines the window size. deflate() will then generate raw deflate data with no zlib header or trailer, and will not compute a check value. windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. The gzip header will have no file name, no extra data, no comment, no modification time (set to zero), no header crc, and the operating system will be set to the appropriate value, if the operating system was determined at compile time. If a gzip stream is being written, strm->adler is a CRC-32 instead of an Adler-32. For raw deflate or gzip encoding, a request for a 256-byte window is rejected as invalid, since only the zlib header provides a means of transmitting the window size to the decompressor. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length encoding). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. When using the zlib format, this function must be called immediately after deflateInit, deflateInit2 or deflateReset, and before any call of deflate. When doing raw deflate, this function must be called either before any call of deflate, or immediately after the completion of a deflate block, i.e. after all input has been consumed and all output has been delivered when using any of the flush options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be discarded, for example if the dictionary is larger than the window size provided in deflateInit or deflateInit2. Thus the strings most likely to be useful should be put at the end of the dictionary, not at the front. In addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. Upon return of this function, strm->adler is set to the Adler-32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The Adler-32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the Adler-32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream or if not at a block boundary for raw deflate). deflateSetDictionary does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); /* Returns the sliding dictionary being maintained by deflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If deflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similary, if dictLength is Z_NULL, then it is not set. deflateGetDictionary() may return a length less than the window size, even when more than the window size in input has been provided. It may return up to 258 bytes less in that case, due to how zlib's implementation of deflate manages the sliding window and lookahead for matches, where matches can be up to 258 bytes long. If the application needs the last window-size bytes of input, then that would need to be saved by the application outside of zlib. deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate the internal compression state. The stream will leave the compression level and any other attributes that may have been set unchanged. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, int level, int strategy)); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2(). This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression approach (which is a function of the level) or the strategy is changed, and if any input has been consumed in a previous deflate() call, then the input available so far is compressed with the old level and strategy using deflate(strm, Z_BLOCK). There are three approaches for the compression levels 0, 1..3, and 4..9 respectively. The new level and strategy will take effect at the next call of deflate(). If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does not have enough output space to complete, then the parameter change will not take effect. In this case, deflateParams() can be called again with the same parameters and more output space to try again. In order to assure a change in the parameters on the first try, the deflate stream should be flushed using deflate() with Z_BLOCK or other flush request until strm.avail_out is not zero, before calling deflateParams(). Then no more input data should be provided before the deflateParams() call. If this is done, the old level and strategy will be applied to the data compressed before deflateParams(), and the new level and strategy will be applied to the the data compressed after deflateParams(). deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if there was not enough output space to complete the compression of the available input data before a change in the strategy or approach. Note that in the case of a Z_BUF_ERROR, the parameters are not changed. A return value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be retried with more output space. */ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain)); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for searching for the best matching string, and even then only by the most fanatic optimizer trying to squeeze out the last compressed bit for their specific input data. Read the deflate.c source code for the meaning of the max_lazy, good_length, nice_length, and max_chain parameters. deflateTune() can be called after deflateInit() or deflateInit2(), and returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, uLong sourceLen)); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or deflateInit2(), and after deflateSetHeader(), if used. This would be used to allocate an output buffer for deflation in a single pass, and so would be called before deflate(). If that first deflate() call is provided the sourceLen input bytes, an output buffer allocated to the size returned by deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed to return Z_STREAM_END. Note that it is possible for the compressed size to be larger than the value returned by deflateBound() if flush options other than Z_FINISH or Z_NO_FLUSH are used. */ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, unsigned *pending, int *bits)); /* deflatePending() returns the number of bytes and bits of output that have been generated, but not yet provided in the available output. The bytes not provided would be due to the available output space having being consumed. The number of bits of output not provided are between 0 and 7, where they await more bits to join them in order to fill out a full byte. If pending or bits are Z_NULL, then those values are not set. deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, int bits, int value)); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits leftover from a previous deflate stream when appending to it. As such, this function can only be used for raw deflate, and must be used before the first deflate() call after a deflateInit2() or deflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the output. deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gz_headerp head)); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called after deflateInit2() or deflateReset() and before the first call of deflate(). The text, time, os, extra field, name, and comment information in the provided gz_header structure are written to the gzip header (xflag is ignored -- the extra flags are set according to the compression level). The caller must assure that, if not Z_NULL, name and comment are terminated with a zero byte, and that if extra is not Z_NULL, that extra_len bytes are available there. If hcrc is true, a gzip header crc is included. Note that the current versions of the command-line version of gzip (up through version 1.3.x) do not support header crc's, and will report that it is a "multi-part gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, the time set to zero, and os set to 255, with no extra, name, or comment fields. The gzip header is returned to the default state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, int windowBits)); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. windowBits can also be zero to request that inflate use the window size in the zlib header of the compressed stream. windowBits can also be -8..-15 for raw inflate. In this case, -windowBits determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is recommended that a check value such as an Adler-32 or a CRC-32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see below), inflate() will not automatically decode concatenated gzip streams. inflate() will return Z_STREAM_END at the end of the gzip stream. The state would need to be reset to continue decoding a subsequent gzip stream. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit2 does not perform any decompression apart from possibly reading the zlib header if present: actual decompression will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unused and unchanged.) The current implementation of inflateInit2() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the Adler-32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called at any time to set the dictionary. If the provided dictionary is smaller than the window and there is already data in the window, then the provided dictionary will amend what's there. The application must insure that the dictionary that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect Adler-32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If inflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similary, if dictLength is Z_NULL, then it is not set. inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); /* Skips invalid compressed data until a possible full flush point (see above for the description of deflate with Z_FULL_FLUSH) can be found, or until all available input is skipped. No output is provided. inflateSync searches for a 00 00 FF FF pattern in the compressed data. All full flush points have this pattern, but not all occurrences of this pattern are full flush points. inflateSync returns Z_OK if a possible full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the success case, the application may save the current current value of total_in which indicates where valid compressed data was found. In the error case, the application may repeatedly call inflateSync, providing more input each time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when randomly accessing a large stream. The first pass through the stream can periodically record the inflate state, allowing restarting inflate at those points when randomly accessing the stream. inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, int windowBits)); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted the same as it is for inflateInit2. If the window size is changed, then the memory allocated for the window is freed, and the window will be reallocated by inflate() if needed. inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL), or if the windowBits parameter is invalid. */ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, int bits, int value)); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the middle of a byte. The provided bits will be used before any bytes are used from next_in. This function should only be used with raw inflate, and should be used before the first inflate() call after inflateInit2() or inflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the input. If bits is negative, then the input stream bit buffer is emptied. Then inflatePrime() can be called again to put bits in the buffer. This is used to clear out bits leftover after feeding inflate a block description prior to feeding inflate codes. inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the return value down 16 bits. If the upper value is -1 and the lower value is zero, then inflate() is currently decoding information outside of a block. If the upper value is -1 and the lower value is non-zero, then inflate is in the middle of a stored block, with the lower value equaling the number of bytes from the input remaining to copy. If the upper value is not -1, then it is the number of bits back from the current bit position in the input of the code (literal or length/distance pair) currently being processed. In that case the lower value is the number of bytes already emitted for that code. A code is being processed if inflate is waiting for more input to complete decoding of the code, or if it has completed decoding but is waiting for more output space to write the literal or match data. inflateMark() is used to mark locations in the input data for random access, which may be at bit positions, and to note those cases where the output of a code may span boundaries of random access blocks. The current location in the input stream can be determined from avail_in and data_type as noted in the description for the Z_BLOCK flush parameter for inflate. inflateMark returns the value noted above, or -65536 if the provided source stream state was inconsistent. */ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, gz_headerp head)); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after inflateInit2() or inflateReset(), and before the first call of inflate(). As inflate() processes the gzip stream, head->done is zero until the header is completed, at which time head->done is set to one. If a zlib stream is being decoded, then head->done is set to -1 to indicate that there will be no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be used to force inflate() to return immediately after header processing is complete and before any actual data is decompressed. The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC was valid if done is set to one.) If extra is not Z_NULL, then extra_max contains the maximum number of bytes to write to extra. Once done is true, extra_len contains the actual extra field length, and extra contains the extra field, or that field truncated if extra_max is less than extra_len. If name is not Z_NULL, then up to name_max characters are written there, terminated with a zero unless the length is greater than name_max. If comment is not Z_NULL, then up to comm_max characters are written there, terminated with a zero unless the length is greater than comm_max. When any of extra, name, or comment are not Z_NULL and the respective field is not present in the header, then that field is set to Z_NULL to signal its absence. This allows the use of deflateSetHeader() with the returned structure to duplicate the header. However if those fields are set to allocated memory, then the application will need to save those pointers elsewhere so that they can be eventually freed. If inflateGetHeader is not used, then the header information is simply discarded. The header is always checked for validity, including the header CRC if present. inflateReset() will reset the process to discard the header information. The application would need to call inflateGetHeader() again to retrieve the header from the next gzip stream. inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, unsigned char FAR *window)); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized before the call. If zalloc and zfree are Z_NULL, then the default library- derived memory allocation routines are used. windowBits is the base two logarithm of the window size, in the range 8..15. window is a caller supplied buffer of that size. Except for special applications where it is assured that deflate was used with small window sizes, windowBits must be 15 and a 32K byte window must be supplied to be able to decompress general deflate streams. See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of the parameters are invalid, Z_MEM_ERROR if the internal state could not be allocated, or Z_VERSION_ERROR if the version of the library does not match the version of the header file. */ typedef unsigned (*in_func) OF((void FAR *, z_const unsigned char FAR * FAR *)); typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc)); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is potentially more efficient than inflate() for file i/o applications, in that it avoids copying between the output and the sliding window by simply making the window itself the output buffer. inflate() can be faster on modern CPUs when used with large buffers. inflateBack() trusts the application to not change the output buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. inflateBack() may then be used multiple times to inflate a complete, raw deflate stream with each call. inflateBackEnd() is then called to free the allocated state. A raw deflate stream is one with no zlib or gzip header or trailer. This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the header and process the trailer on its own, hence this routine expects only the raw deflate stream to decompress. This is different from the default behavior of inflate(), which expects a zlib header and trailer around the deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those routines until it reads a complete deflate stream and writes out all of the uncompressed data, or until it encounters an error. The function's parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If there is no input available, in() must return zero -- buf is ignored in that case -- and inflateBack() will return a buffer error. inflateBack() will call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() should return zero on success, or non-zero on failure. If out() returns non-zero, inflateBack() will return with an error. Neither in() nor out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). For convenience, inflateBack() can be provided input on the first call by setting strm->next_in and strm->avail_in. If that input is exhausted, then in() will be called. Therefore strm->next_in must be initialized before calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in must also be initialized, and then if strm->avail_in is not zero, input will initially be taken from strm->next_in[0 .. strm->avail_in - 1]. The in_desc and out_desc parameters of inflateBack() is passed as the first parameter of in() and out() respectively when they are called. These descriptors can be optionally used to pass any information that the caller- supplied in() and out() functions need to do their job. On return, inflateBack() will set strm->next_in and strm->avail_in to pass back any unused input that was provided by the last in() call. The return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR if in() or out() returned an error, Z_DATA_ERROR if there was a format error in the deflate stream (in which case strm->msg is set to indicate the nature of the error), or Z_STREAM_ERROR if the stream was not properly initialized. In the case of Z_BUF_ERROR, an input or output error can be distinguished using strm->next_in which will be Z_NULL only if in() returned an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. */ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); /* All memory allocated by inflateBackInit() is freed. inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream state was inconsistent. */ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: 1.0: size of uInt 3.2: size of uLong 5.4: size of voidpf (pointer) 7.6: size of z_off_t Compiler, assembler, and debug options: 8: ZLIB_DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) One-time table building (smaller code, but not thread-safe if true): 12: BUILDFIXED -- build static block decoding tables when needed 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed 14,15: 0 (reserved) Library content (indicates missing functionality): 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking deflate code when not needed) 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect and decode gzip streams (to avoid linking crc code) 18-19: 0 (reserved) Operation variations (changes in library functionality): 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate 21: FASTEST -- deflate algorithm with only one, lowest compression level 22,23: 0 (reserved) The sprintf variant used by gzprintf (zero is best): 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! 26: 0 = returns value, 1 = void -- 1 means inferred string length returned Remainder: 27-31: 0 (reserved) */ #ifndef Z_SOLO /* utility functions */ /* The following utility functions are implemented on top of the basic stream-oriented functions. To simplify the interface, some default options are assumed (compression level and memory usage, standard memory allocation functions). The source code of these utility functions can be modified if you need special options. */ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed data. compress() is equivalent to compress2() with a level parameter of Z_DEFAULT_COMPRESSION. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. */ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed data. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the uncompressed data. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In the case where there is not enough room, uncompress() will fill the output buffer with the uncompressed data up to that point. */ ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong *sourceLen)); /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of source bytes consumed. */ /* gzip file access functions */ /* This library supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio, using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. */ typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); Opens a gzip (.gz) file for reading or writing. The mode parameter is as in fopen ("rb" or "wb") but can also include a compression level ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of deflateInit2 for more information about the strategy parameter.) 'T' will request transparent writing or appending with no compression and not using the gzip format. "a" can be used instead of "w" to request that the gzip stream that will be written be appended to the file. "+" will result in an error, since reading and writing to the same gzip file is not supported. The addition of "x" when writing will create the file exclusively, which fails if the file already exists. On systems that support it, the addition of "e" when reading or writing will set the flag to close the file on an execve() call. These functions, as well as gzip, will read and decode a sequence of gzip streams in a file. The append function of gzopen() can be used to create such a file. (Also see gzflush() for another way to do this.) When appending, gzopen does not test whether the file begins with a gzip stream, nor does it look for the end of the gzip streams to begin appending. gzopen will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this case gzread will directly read from the file without decompression. When reading, this will be detected automatically by looking for the magic two- byte gzip header. gzopen returns NULL if the file could not be opened, if there was insufficient memory to allocate the gzFile state, or if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). errno can be checked to determine if the reason gzopen failed was that the file could not be opened. */ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); /* gzdopen associates a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has been previously opened with fopen). The mode parameter is as in gzopen. The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, mode);. The duplicated descriptor should be saved to avoid a leak, since gzdopen does not close fd if it fails. If you are using fileno() to get the file descriptor from a FILE *, then you will have to use dup() to avoid double-close()ing the file descriptor. Both gzclose() and fclose() will close the associated file descriptor, so they need to have different file descriptors. gzdopen returns NULL if there was insufficient memory to allocate the gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided), or if fd is -1. The file descriptor is not used until the next gz* read, write, seek, or close operation, so gzdopen will not detect if fd is invalid (unless fd is -1). */ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); /* Set the internal buffer size used by this library's functions. The default buffer size is 8192 bytes. This function must be called after gzopen() or gzdopen(), and before any other calls that read or write the file. The buffer memory allocation is always deferred to the first read or write. Three times that size in buffer space is allocated. A larger buffer size of, for example, 64K or 128K bytes will noticeably increase the speed of decompression (reading). The new buffer size also affects the maximum length for gzprintf(). gzbuffer() returns 0 on success, or -1 on failure, such as being called too late. */ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); /* Dynamically update the compression level or strategy. See the description of deflateInit2 for the meaning of these parameters. Previously provided data is flushed before the parameter change. gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not opened for writing, Z_ERRNO if there is an error writing the flushed data, or Z_MEM_ERROR if there is a memory allocation error. */ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); /* Reads the given number of uncompressed bytes from the compressed file. If the input file is not in gzip format, gzread copies the given number of bytes into the buffer directly from the file. After reaching the end of a gzip stream in the input, gzread will continue to read, looking for another gzip stream. Any number of gzip streams may be concatenated in the input file, and will all be decompressed by gzread(). If something other than a gzip stream is encountered after a gzip stream, that remaining trailing garbage is ignored (and no error is returned). gzread can be used to read a gzip file that is being concurrently written. Upon reaching the end of the input, gzread will return with the available data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then gzclearerr can be used to clear the end of file indicator in order to permit gzread to be tried again. Z_OK indicates that a gzip stream was completed on the last gzread. Z_BUF_ERROR indicates that the input file ended in the middle of a gzip stream. Note that gzread does not return -1 in the event of an incomplete gzip stream. This error is deferred until gzclose(), which will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip stream. Alternatively, gzerror can be used before gzclose to detect this case. gzread returns the number of uncompressed bytes actually read, less than len for end of file, or -1 for error. If len is too large to fit in an int, then nothing is read, -1 is returned, and the error state is set to Z_STREAM_ERROR. */ ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, gzFile file)); /* Read up to nitems items of size size from file to buf, otherwise operating as gzread() does. This duplicates the interface of stdio's fread(), with size_t request and return types. If the library defines size_t, then z_size_t is identical to size_t. If not, then z_size_t is an unsigned integer type that can contain a pointer. gzfread() returns the number of full items read of size size, or zero if the end of the file was reached and a full item could not be read, or if there was an error. gzerror() must be consulted if zero is returned in order to determine if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is read, zero is returned, and the error state is set to Z_STREAM_ERROR. In the event that the end of file is reached and only a partial item is available at the end, i.e. the remaining uncompressed data length is not a multiple of size, then the final partial item is nevetheless read into buf and the end-of-file flag is set. The length of the partial item read is not provided, but could be inferred from the result of gztell(). This behavior is the same as the behavior of fread() implementations in common libraries, but it prevents the direct use of gzfread() to read a concurrently written file, reseting and retrying on end-of-file, when size is not 1. */ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); /* Writes the given number of uncompressed bytes into the compressed file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, z_size_t nitems, gzFile file)); /* gzfwrite() writes nitems items of size size from buf to file, duplicating the interface of stdio's fwrite(), with size_t request and return types. If the library defines size_t, then z_size_t is identical to size_t. If not, then z_size_t is an unsigned integer type that can contain a pointer. gzfwrite() returns the number of full items written of size size, or zero if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is written, zero is returned, and the error state is set to Z_STREAM_ERROR. */ ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); /* Converts, formats, and writes the arguments to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of uncompressed bytes actually written, or a negative zlib error code in case of error. The number of uncompressed bytes written is limited to 8191, or one less than the buffer size given to gzbuffer(). The caller should assure that this limit is not exceeded. If it is exceeded, then gzprintf() will return an error (0) with nothing written. In this case, there may also be a buffer overflow with unpredictable consequences, which is possible only if zlib was compiled with the insecure functions sprintf() or vsprintf() because the secure snprintf() or vsnprintf() functions were not available. This can be determined using zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); /* Writes the given null-terminated string to the compressed file, excluding the terminating null character. gzputs returns the number of characters written, or -1 in case of error. */ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); /* Reads bytes from the compressed file until len-1 characters are read, or a newline character is read and transferred to buf, or an end-of-file condition is encountered. If any characters are read or if len == 1, the string is terminated with a null character. If no characters are read due to an end-of-file or len < 1, then the buffer is left untouched. gzgets returns buf which is a null-terminated string, or it returns NULL for end-of-file or in case of error. If there was an error, the contents at buf are indeterminate. */ ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); /* Writes c, converted to an unsigned char, into the compressed file. gzputc returns the value that was written, or -1 in case of error. */ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); /* Reads one byte from the compressed file. gzgetc returns this byte or -1 in case of end of file or error. This is implemented as a macro for speed. As such, it does not do all of the checking the other functions do. I.e. it does not check to see if file is NULL, nor whether the structure file points to has been clobbered or not. */ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); /* Push one character back onto the stream to be read as the first character on the next read. At least one character of push-back is allowed. gzungetc() returns the character pushed, or -1 on failure. gzungetc() will fail if c is -1, and may fail if a character has been pushed but not read yet. If gzungetc is used immediately after gzopen or gzdopen, at least the output buffer size of pushed characters is allowed. (See gzbuffer above.) The pushed character will be discarded if the stream is repositioned with gzseek() or gzrewind(). */ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); /* Flushes all pending output into the compressed file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function gzerror below). gzflush is only permitted when writing. If the flush parameter is Z_FINISH, the remaining data is written and the gzip stream is completed in the output. If gzwrite() is called again, a new gzip stream will be started in the output. gzread() is able to read such concatenated gzip streams. gzflush should be called only when strictly necessary because it will degrade compression if called too often. */ /* ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, z_off_t offset, int whence)); Sets the starting position for the next gzread or gzwrite on the given compressed file. The offset represents a number of bytes in the uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. If the file is opened for reading, this function is emulated but can be extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. */ ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); /* Rewinds the given file. This function is supported only for reading. gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) */ /* ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); Returns the starting position for the next gzread or gzwrite on the given compressed file. This position represents a number of bytes in the uncompressed data stream, and is zero when starting, even if appending or reading a gzip stream from the middle of a file using gzdopen(). gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) */ /* ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); Returns the current offset in the file being read or written. This offset includes the count of bytes that precede the gzip stream, for example when appending or when using gzdopen() for reading. When reading, the offset does not include as yet unused buffered input. This information can be used for a progress indicator. On error, gzoffset() returns -1. */ ZEXTERN int ZEXPORT gzeof OF((gzFile file)); /* Returns true (1) if the end-of-file indicator has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set only if the read tried to go past the end of the input, but came up short. Therefore, just like feof(), gzeof() may return false even if there is no more data to read, in the event that the last read request was for the exact number of bytes remaining in the input file. This will happen if the input file size is an exact multiple of the buffer size. If gzeof() returns true, then the read functions will return no more data, unless the end-of-file indicator is reset by gzclearerr() and the input file has grown since the previous end of file was detected. */ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); /* Returns true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. If the input file is empty, gzdirect() will return true, since the input does not contain a gzip stream. If gzdirect() is used immediately after gzopen() or gzdopen() it will cause buffers to be allocated to allow reading the file to determine if it is a gzip file. Therefore if gzbuffer() is used, it should be called before gzdirect(). When writing, gzdirect() returns true (1) if transparent writing was requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: gzdirect() is not needed when writing. Transparent writing must be explicitly requested, so the application already knows the answer. When linking statically, using gzdirect() will include all of the zlib code for gzip file reading and decompression, which may not be desired.) */ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); /* Flushes all pending output if necessary, closes the compressed file and deallocates the (de)compression state. Note that once file is closed, you cannot call gzerror with file, since its structures have been deallocated. gzclose must not be called more than once on the same file, just as free must not be called more than once on the same allocation. gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the last read ended in the middle of a gzip stream, or Z_OK on success. */ ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to using these instead of gzclose() is that they avoid linking in zlib compression or decompression code that is not used when only reading or only writing respectively. If gzclose() is used, then both compression and decompression code will be included the application when linking to a static zlib library. */ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); /* Returns the error message for the last error which occurred on the given compressed file. errnum is set to zlib error number. If an error occurred in the file system and not in the compression library, errnum is set to Z_ERRNO and the application may consult errno to get the exact error code. The application must not modify the returned string. Future calls to this function may invalidate the previously returned string. If file is closed, then the string previously returned by gzerror will no longer be available. gzerror() should be used to distinguish errors from end-of-file for those functions above that do not distinguish those cases in their return values. */ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); /* Clears the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip file that is being written concurrently. */ #endif /* !Z_SOLO */ /* checksum functions */ /* These functions are not related to compression but are exported anyway because they might be useful in applications using the compression library. */ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. If buf is Z_NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed much faster. Usage example: uLong adler = adler32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { adler = adler32(adler, buffer, length); } if (adler != original_adler) error(); */ ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, z_size_t len)); /* Same as adler32(), but with a size_t length. */ /* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note that the z_off_t type (like off_t) is a signed integer. If len2 is negative, the result has no meaning or utility. */ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. If buf is Z_NULL, this function returns the required initial value for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. Usage example: uLong crc = crc32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { crc = crc32(crc, buffer, length); } if (crc != original_crc) error(); */ ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, z_size_t len)); /* Same as crc32(), but with a size_t length. */ /* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and len2. */ /* various hacks, don't look :) */ /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, const char *version, int stream_size)); ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size)); #ifdef Z_PREFIX_SET # define z_deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) # define z_inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #else # define deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) # define inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) # define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) # define inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) # define inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #endif #ifndef Z_SOLO /* gzgetc() macro and its supporting function and exposed data structure. Note * that the real internal state is much larger than the exposed structure. * This abbreviated structure exposes just enough for the gzgetc() macro. The * user should not mess with these exposed elements, since their names or * behavior could change in the future, perhaps even capriciously. They can * only be used by the gzgetc() macro. You have been warned. */ struct gzFile_s { unsigned have; unsigned char *next; z_off64_t pos; }; ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #else # define gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #endif /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if * both are true, the application gets the *64 functions, and the regular * functions are changed to 64 bits) -- in case these are set on systems * without large file support, _LFS64_LARGEFILE must also be true */ #ifdef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); #endif #if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) # ifdef Z_PREFIX_SET # define z_gzopen z_gzopen64 # define z_gzseek z_gzseek64 # define z_gztell z_gztell64 # define z_gzoffset z_gzoffset64 # define z_adler32_combine z_adler32_combine64 # define z_crc32_combine z_crc32_combine64 # else # define gzopen gzopen64 # define gzseek gzseek64 # define gztell gztell64 # define gzoffset gzoffset64 # define adler32_combine adler32_combine64 # define crc32_combine crc32_combine64 # endif # ifndef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); # endif #else ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif #else /* Z_SOLO */ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif /* !Z_SOLO */ /* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, const char *format, va_list va)); # endif #endif #ifdef __cplusplus } #endif #endif /* ZLIB_H */ ================================================ FILE: deps/CascLib/src/zlib/zutil.c ================================================ /* zutil.c -- target dependent utility functions for the compression library * Copyright (C) 1995-2017 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" #ifndef Z_SOLO # include "gzguts.h" #endif z_const char * const z_errmsg[10] = { (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */ (z_const char *)"stream end", /* Z_STREAM_END 1 */ (z_const char *)"", /* Z_OK 0 */ (z_const char *)"file error", /* Z_ERRNO (-1) */ (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */ (z_const char *)"data error", /* Z_DATA_ERROR (-3) */ (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */ (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */ (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */ (z_const char *)"" }; const char * ZEXPORT zlibVersion() { return ZLIB_VERSION; } uLong ZEXPORT zlibCompileFlags() { uLong flags; flags = 0; switch ((int)(sizeof(uInt))) { case 2: break; case 4: flags += 1; break; case 8: flags += 2; break; default: flags += 3; } switch ((int)(sizeof(uLong))) { case 2: break; case 4: flags += 1 << 2; break; case 8: flags += 2 << 2; break; default: flags += 3 << 2; } switch ((int)(sizeof(voidpf))) { case 2: break; case 4: flags += 1 << 4; break; case 8: flags += 2 << 4; break; default: flags += 3 << 4; } switch ((int)(sizeof(z_off_t))) { case 2: break; case 4: flags += 1 << 6; break; case 8: flags += 2 << 6; break; default: flags += 3 << 6; } #ifdef ZLIB_DEBUG flags += 1 << 8; #endif #if defined(ASMV) || defined(ASMINF) flags += 1 << 9; #endif #ifdef ZLIB_WINAPI flags += 1 << 10; #endif #ifdef BUILDFIXED flags += 1 << 12; #endif #ifdef DYNAMIC_CRC_TABLE flags += 1 << 13; #endif #ifdef NO_GZCOMPRESS flags += 1L << 16; #endif #ifdef NO_GZIP flags += 1L << 17; #endif #ifdef PKZIP_BUG_WORKAROUND flags += 1L << 20; #endif #ifdef FASTEST flags += 1L << 21; #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifdef NO_vsnprintf flags += 1L << 25; # ifdef HAS_vsprintf_void flags += 1L << 26; # endif # else # ifdef HAS_vsnprintf_void flags += 1L << 26; # endif # endif #else flags += 1L << 24; # ifdef NO_snprintf flags += 1L << 25; # ifdef HAS_sprintf_void flags += 1L << 26; # endif # else # ifdef HAS_snprintf_void flags += 1L << 26; # endif # endif #endif return flags; } #ifdef ZLIB_DEBUG #include # ifndef verbose # define verbose 0 # endif int ZLIB_INTERNAL z_verbose = verbose; void ZLIB_INTERNAL z_error (m) char *m; { fprintf(stderr, "%s\n", m); exit(1); } #endif /* exported to allow conversion of error code to string for compress() and * uncompress() */ const char * ZEXPORT zError(err) int err; { return ERR_MSG(err); } #if defined(_WIN32_WCE) /* The Microsoft C Run-Time Library for Windows CE doesn't have * errno. We define it as a global variable to simplify porting. * Its value is always 0 and should not be used. */ int errno = 0; #endif #ifndef HAVE_MEMCPY void ZLIB_INTERNAL zmemcpy(dest, source, len) Bytef* dest; const Bytef* source; uInt len; { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } int ZLIB_INTERNAL zmemcmp(s1, s2, len) const Bytef* s1; const Bytef* s2; uInt len; { uInt j; for (j = 0; j < len; j++) { if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; } return 0; } void ZLIB_INTERNAL zmemzero(dest, len) Bytef* dest; uInt len; { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ } while (--len != 0); } #endif #ifndef Z_SOLO #ifdef SYS16BIT #ifdef __TURBOC__ /* Turbo C in 16-bit mode */ # define MY_ZCALLOC /* Turbo C malloc() does not allow dynamic allocation of 64K bytes * and farmalloc(64K) returns a pointer with an offset of 8, so we * must fix the pointer. Warning: the pointer must be put back to its * original form in order to free it, use zcfree(). */ #define MAX_PTR 10 /* 10*64K = 640K */ local int next_ptr = 0; typedef struct ptr_table_s { voidpf org_ptr; voidpf new_ptr; } ptr_table; local ptr_table table[MAX_PTR]; /* This table is used to remember the original form of pointers * to large buffers (64K). Such pointers are normalized with a zero offset. * Since MSDOS is not a preemptive multitasking OS, this table is not * protected from concurrent access. This hack doesn't work anyway on * a protected system like OS/2. Use Microsoft C instead. */ voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) { voidpf buf; ulg bsize = (ulg)items*size; (void)opaque; /* If we allocate less than 65520 bytes, we assume that farmalloc * will return a usable pointer which doesn't have to be normalized. */ if (bsize < 65520L) { buf = farmalloc(bsize); if (*(ush*)&buf != 0) return buf; } else { buf = farmalloc(bsize + 16L); } if (buf == NULL || next_ptr >= MAX_PTR) return NULL; table[next_ptr].org_ptr = buf; /* Normalize the pointer to seg:0 */ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; *(ush*)&buf = 0; table[next_ptr++].new_ptr = buf; return buf; } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { int n; (void)opaque; if (*(ush*)&ptr != 0) { /* object < 64K */ farfree(ptr); return; } /* Find the original pointer */ for (n = 0; n < next_ptr; n++) { if (ptr != table[n].new_ptr) continue; farfree(table[n].org_ptr); while (++n < next_ptr) { table[n-1] = table[n]; } next_ptr--; return; } Assert(0, "zcfree: ptr not found"); } #endif /* __TURBOC__ */ #ifdef M_I86 /* Microsoft C in 16-bit mode */ # define MY_ZCALLOC #if (!defined(_MSC_VER) || (_MSC_VER <= 600)) # define _halloc halloc # define _hfree hfree #endif voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) { (void)opaque; return _halloc((long)items, size); } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { (void)opaque; _hfree(ptr); } #endif /* M_I86 */ #endif /* SYS16BIT */ #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC extern voidp malloc OF((uInt size)); extern voidp calloc OF((uInt items, uInt size)); extern void free OF((voidpf ptr)); #endif voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) voidpf opaque; unsigned items; unsigned size; { (void)opaque; return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } void ZLIB_INTERNAL zcfree (opaque, ptr) voidpf opaque; voidpf ptr; { (void)opaque; free(ptr); } #endif /* MY_ZCALLOC */ #endif /* !Z_SOLO */ ================================================ FILE: deps/CascLib/src/zlib/zutil.h ================================================ /* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef ZUTIL_H #define ZUTIL_H #ifdef HAVE_HIDDEN # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include "zlib.h" #if defined(STDC) && !defined(Z_SOLO) # if !(defined(_WIN32_WCE) && defined(_MSC_VER)) # include # endif # include # include #endif #ifdef Z_SOLO typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ #endif #ifndef local # define local static #endif /* since "static" is used to mean two completely different things in C, we define "local" for the non-static meaning of "static", for readability (compile with -Dlocal if your debugger can't find static symbols) */ typedef unsigned char uch; typedef uch FAR uchf; typedef unsigned short ush; typedef ush FAR ushf; typedef unsigned long ulg; extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ return (strm->msg = ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default memLevel */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ #define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ /* target dependencies */ #if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) # define OS_CODE 0x00 # ifndef Z_SOLO # if defined(__TURBOC__) || defined(__BORLANDC__) # if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) /* Allow compilation with ANSI keywords only enabled */ void _Cdecl farfree( void *block ); void *_Cdecl farmalloc( unsigned long nbytes ); # else # include # endif # else /* MSC or DJGPP */ # include # endif # endif #endif #ifdef AMIGA # define OS_CODE 1 #endif #if defined(VAXC) || defined(VMS) # define OS_CODE 2 # define F_OPEN(name, mode) \ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") #endif #ifdef __370__ # if __TARGET_LIB__ < 0x20000000 # define OS_CODE 4 # elif __TARGET_LIB__ < 0x40000000 # define OS_CODE 11 # else # define OS_CODE 8 # endif #endif #if defined(ATARI) || defined(atarist) # define OS_CODE 5 #endif #ifdef OS2 # define OS_CODE 6 # if defined(M_I86) && !defined(Z_SOLO) # include # endif #endif #if defined(MACOS) || defined(TARGET_OS_MAC) # define OS_CODE 7 # ifndef Z_SOLO # if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os # include /* for fdopen */ # else # ifndef fdopen # define fdopen(fd,mode) NULL /* No fdopen() */ # endif # endif # endif #endif #ifdef __acorn # define OS_CODE 13 #endif #if defined(WIN32) && !defined(__CYGWIN__) # define OS_CODE 10 #endif #ifdef _BEOS_ # define OS_CODE 16 #endif #ifdef __TOS_OS400__ # define OS_CODE 18 #endif #ifdef __APPLE__ # define OS_CODE 19 #endif #if defined(_BEOS_) || defined(RISCOS) # define fdopen(fd,mode) NULL /* No fdopen() */ #endif #if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX # if defined(_WIN32_WCE) # define fdopen(fd,mode) NULL /* No fdopen() */ # ifndef _PTRDIFF_T_DEFINED typedef int ptrdiff_t; # define _PTRDIFF_T_DEFINED # endif # else # define fdopen(fd,type) _fdopen(fd,type) # endif #endif #if defined(__BORLANDC__) && !defined(MSDOS) #pragma warn -8004 #pragma warn -8008 #pragma warn -8066 #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_WIN32) && \ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); #endif /* common defaults */ #ifndef OS_CODE # define OS_CODE 3 /* assume Unix */ #endif #ifndef F_OPEN # define F_OPEN(name, mode) fopen((name), (mode)) #endif /* functions */ #if defined(pyr) || defined(Z_SOLO) # define NO_MEMCPY #endif #if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) /* Use our own functions for small and medium model with MSC <= 5.0. * You may have to use the same strategy for Borland C (untested). * The __SC__ check is for Symantec. */ # define NO_MEMCPY #endif #if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) # define HAVE_MEMCPY #endif #ifdef HAVE_MEMCPY # ifdef SMALL_MEDIUM /* MSDOS small or medium model */ # define zmemcpy _fmemcpy # define zmemcmp _fmemcmp # define zmemzero(dest, len) _fmemset(dest, 0, len) # else # define zmemcpy memcpy # define zmemcmp memcmp # define zmemzero(dest, len) memset(dest, 0, len) # endif #else void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); #endif /* Diagnostic functions */ #ifdef ZLIB_DEBUG # include extern int ZLIB_INTERNAL z_verbose; extern void ZLIB_INTERNAL z_error OF((char *m)); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} # define Tracevv(x) {if (z_verbose>1) fprintf x ;} # define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} # define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif #ifndef Z_SOLO voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, unsigned size)); void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); #endif #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} /* Reverse the bytes in a 32-bit value */ #define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) #endif /* ZUTIL_H */ ================================================ FILE: deps/glad/CMakeLists.txt ================================================ add_library(glad STATIC EXCLUDE_FROM_ALL src/glad.c src/glad_wgl.c) target_include_directories(glad PUBLIC include) ================================================ FILE: deps/glad/include/KHR/khrplatform.h ================================================ #ifndef __khrplatform_h_ #define __khrplatform_h_ /* ** Copyright (c) 2008-2018 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the ** "Materials"), to deal in the Materials without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Materials, and to ** permit persons to whom the Materials are furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Materials. ** ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ /* Khronos platform-specific types and definitions. * * The master copy of khrplatform.h is maintained in the Khronos EGL * Registry repository at https://github.com/KhronosGroup/EGL-Registry * The last semantic modification to khrplatform.h was at commit ID: * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 * * Adopters may modify this file to suit their platform. Adopters are * encouraged to submit platform specific modifications to the Khronos * group so that they can be included in future versions of this file. * Please submit changes by filing pull requests or issues on * the EGL Registry repository linked above. * * * See the Implementer's Guidelines for information about where this file * should be located on your system and for more details of its use: * http://www.khronos.org/registry/implementers_guide.pdf * * This file should be included as * #include * by Khronos client API header files that use its types and defines. * * The types in khrplatform.h should only be used to define API-specific types. * * Types defined in khrplatform.h: * khronos_int8_t signed 8 bit * khronos_uint8_t unsigned 8 bit * khronos_int16_t signed 16 bit * khronos_uint16_t unsigned 16 bit * khronos_int32_t signed 32 bit * khronos_uint32_t unsigned 32 bit * khronos_int64_t signed 64 bit * khronos_uint64_t unsigned 64 bit * khronos_intptr_t signed same number of bits as a pointer * khronos_uintptr_t unsigned same number of bits as a pointer * khronos_ssize_t signed size * khronos_usize_t unsigned size * khronos_float_t signed 32 bit floating point * khronos_time_ns_t unsigned 64 bit time in nanoseconds * khronos_utime_nanoseconds_t unsigned time interval or absolute time in * nanoseconds * khronos_stime_nanoseconds_t signed time interval in nanoseconds * khronos_boolean_enum_t enumerated boolean type. This should * only be used as a base type when a client API's boolean type is * an enum. Client APIs which use an integer or other type for * booleans cannot use this as the base type for their boolean. * * Tokens defined in khrplatform.h: * * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. * * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. * * Calling convention macros defined in this file: * KHRONOS_APICALL * KHRONOS_APIENTRY * KHRONOS_APIATTRIBUTES * * These may be used in function prototypes as: * * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( * int arg1, * int arg2) KHRONOS_APIATTRIBUTES; */ #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) # define KHRONOS_STATIC 1 #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APICALL *------------------------------------------------------------------------- * This precedes the return type of the function in the function prototype. */ #if defined(KHRONOS_STATIC) /* If the preprocessor constant KHRONOS_STATIC is defined, make the * header compatible with static linking. */ # define KHRONOS_APICALL #elif defined(_WIN32) # define KHRONOS_APICALL __declspec(dllimport) #elif defined (__SYMBIAN32__) # define KHRONOS_APICALL IMPORT_C #elif defined(__ANDROID__) # define KHRONOS_APICALL __attribute__((visibility("default"))) #else # define KHRONOS_APICALL #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APIENTRY *------------------------------------------------------------------------- * This follows the return type of the function and precedes the function * name in the function prototype. */ #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) /* Win32 but not WinCE */ # define KHRONOS_APIENTRY __stdcall #else # define KHRONOS_APIENTRY #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APIATTRIBUTES *------------------------------------------------------------------------- * This follows the closing parenthesis of the function prototype arguments. */ #if defined (__ARMCC_2__) #define KHRONOS_APIATTRIBUTES __softfp #else #define KHRONOS_APIATTRIBUTES #endif /*------------------------------------------------------------------------- * basic type definitions *-----------------------------------------------------------------------*/ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) /* * Using */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(__VMS ) || defined(__sgi) /* * Using */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) /* * Win32 */ typedef __int32 khronos_int32_t; typedef unsigned __int32 khronos_uint32_t; typedef __int64 khronos_int64_t; typedef unsigned __int64 khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(__sun__) || defined(__digital__) /* * Sun or Digital */ typedef int khronos_int32_t; typedef unsigned int khronos_uint32_t; #if defined(__arch64__) || defined(_LP64) typedef long int khronos_int64_t; typedef unsigned long int khronos_uint64_t; #else typedef long long int khronos_int64_t; typedef unsigned long long int khronos_uint64_t; #endif /* __arch64__ */ #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif 0 /* * Hypothetical platform with no float or int64 support */ typedef int khronos_int32_t; typedef unsigned int khronos_uint32_t; #define KHRONOS_SUPPORT_INT64 0 #define KHRONOS_SUPPORT_FLOAT 0 #else /* * Generic fallback */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #endif /* * Types that are (so far) the same on all platforms */ typedef signed char khronos_int8_t; typedef unsigned char khronos_uint8_t; typedef signed short int khronos_int16_t; typedef unsigned short int khronos_uint16_t; /* * Types that differ between LLP64 and LP64 architectures - in LLP64, * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears * to be the only LLP64 architecture in current use. */ #ifdef _WIN64 typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; typedef signed long long int khronos_ssize_t; typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif #if KHRONOS_SUPPORT_FLOAT /* * Float type */ typedef float khronos_float_t; #endif #if KHRONOS_SUPPORT_INT64 /* Time types * * These types can be used to represent a time interval in nanoseconds or * an absolute Unadjusted System Time. Unadjusted System Time is the number * of nanoseconds since some arbitrary system event (e.g. since the last * time the system booted). The Unadjusted System Time is an unsigned * 64 bit value that wraps back to 0 every 584 years. Time intervals * may be either signed or unsigned. */ typedef khronos_uint64_t khronos_utime_nanoseconds_t; typedef khronos_int64_t khronos_stime_nanoseconds_t; #endif /* * Dummy value used to pad enum types to 32 bits. */ #ifndef KHRONOS_MAX_ENUM #define KHRONOS_MAX_ENUM 0x7FFFFFFF #endif /* * Enumerated boolean type * * Values other than zero should be considered to be true. Therefore * comparisons should not be made against KHRONOS_TRUE. */ typedef enum { KHRONOS_FALSE = 0, KHRONOS_TRUE = 1, KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM } khronos_boolean_enum_t; #endif /* __khrplatform_h_ */ ================================================ FILE: deps/glad/include/glad/glad.h ================================================ /* OpenGL loader generated by glad 0.1.34 on Sat Oct 30 06:22:17 2021. Language/Generator: C/C++ Specification: gl APIs: gl=3.3 Profile: core Extensions: Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: --profile="core" --api="gl=3.3" --generator="c" --spec="gl" --extensions="" Online: https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D3.3 */ #ifndef __glad_h_ #define __glad_h_ #ifdef __gl_h_ #error OpenGL header already included, remove this include, glad already provides it #endif #define __gl_h_ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) #define APIENTRY __stdcall #endif #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif #ifndef GLAPIENTRY #define GLAPIENTRY APIENTRY #endif #ifdef __cplusplus extern "C" { #endif struct gladGLversionStruct { int major; int minor; }; typedef void* (* GLADloadproc)(const char *name); #ifndef GLAPI # if defined(GLAD_GLAPI_EXPORT) # if defined(_WIN32) || defined(__CYGWIN__) # if defined(GLAD_GLAPI_EXPORT_BUILD) # if defined(__GNUC__) # define GLAPI __attribute__ ((dllexport)) extern # else # define GLAPI __declspec(dllexport) extern # endif # else # if defined(__GNUC__) # define GLAPI __attribute__ ((dllimport)) extern # else # define GLAPI __declspec(dllimport) extern # endif # endif # elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD) # define GLAPI __attribute__ ((visibility ("default"))) extern # else # define GLAPI extern # endif # else # define GLAPI extern # endif #endif GLAPI struct gladGLversionStruct GLVersion; GLAPI int gladLoadGL(void); GLAPI int gladLoadGLLoader(GLADloadproc); #include typedef unsigned int GLenum; typedef unsigned char GLboolean; typedef unsigned int GLbitfield; typedef void GLvoid; typedef khronos_int8_t GLbyte; typedef khronos_uint8_t GLubyte; typedef khronos_int16_t GLshort; typedef khronos_uint16_t GLushort; typedef int GLint; typedef unsigned int GLuint; typedef khronos_int32_t GLclampx; typedef int GLsizei; typedef khronos_float_t GLfloat; typedef khronos_float_t GLclampf; typedef double GLdouble; typedef double GLclampd; typedef void *GLeglClientBufferEXT; typedef void *GLeglImageOES; typedef char GLchar; typedef char GLcharARB; #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif typedef khronos_uint16_t GLhalf; typedef khronos_uint16_t GLhalfARB; typedef khronos_int32_t GLfixed; typedef khronos_intptr_t GLintptr; typedef khronos_intptr_t GLintptrARB; typedef khronos_ssize_t GLsizeiptr; typedef khronos_ssize_t GLsizeiptrARB; typedef khronos_int64_t GLint64; typedef khronos_int64_t GLint64EXT; typedef khronos_uint64_t GLuint64; typedef khronos_uint64_t GLuint64EXT; typedef struct __GLsync *GLsync; struct _cl_context; struct _cl_event; typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); typedef unsigned short GLhalfNV; typedef GLintptr GLvdpauSurfaceNV; typedef void (APIENTRY *GLVULKANPROCNV)(void); #define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_FALSE 0 #define GL_TRUE 1 #define GL_POINTS 0x0000 #define GL_LINES 0x0001 #define GL_LINE_LOOP 0x0002 #define GL_LINE_STRIP 0x0003 #define GL_TRIANGLES 0x0004 #define GL_TRIANGLE_STRIP 0x0005 #define GL_TRIANGLE_FAN 0x0006 #define GL_NEVER 0x0200 #define GL_LESS 0x0201 #define GL_EQUAL 0x0202 #define GL_LEQUAL 0x0203 #define GL_GREATER 0x0204 #define GL_NOTEQUAL 0x0205 #define GL_GEQUAL 0x0206 #define GL_ALWAYS 0x0207 #define GL_ZERO 0 #define GL_ONE 1 #define GL_SRC_COLOR 0x0300 #define GL_ONE_MINUS_SRC_COLOR 0x0301 #define GL_SRC_ALPHA 0x0302 #define GL_ONE_MINUS_SRC_ALPHA 0x0303 #define GL_DST_ALPHA 0x0304 #define GL_ONE_MINUS_DST_ALPHA 0x0305 #define GL_DST_COLOR 0x0306 #define GL_ONE_MINUS_DST_COLOR 0x0307 #define GL_SRC_ALPHA_SATURATE 0x0308 #define GL_NONE 0 #define GL_FRONT_LEFT 0x0400 #define GL_FRONT_RIGHT 0x0401 #define GL_BACK_LEFT 0x0402 #define GL_BACK_RIGHT 0x0403 #define GL_FRONT 0x0404 #define GL_BACK 0x0405 #define GL_LEFT 0x0406 #define GL_RIGHT 0x0407 #define GL_FRONT_AND_BACK 0x0408 #define GL_NO_ERROR 0 #define GL_INVALID_ENUM 0x0500 #define GL_INVALID_VALUE 0x0501 #define GL_INVALID_OPERATION 0x0502 #define GL_OUT_OF_MEMORY 0x0505 #define GL_CW 0x0900 #define GL_CCW 0x0901 #define GL_POINT_SIZE 0x0B11 #define GL_POINT_SIZE_RANGE 0x0B12 #define GL_POINT_SIZE_GRANULARITY 0x0B13 #define GL_LINE_SMOOTH 0x0B20 #define GL_LINE_WIDTH 0x0B21 #define GL_LINE_WIDTH_RANGE 0x0B22 #define GL_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_POLYGON_MODE 0x0B40 #define GL_POLYGON_SMOOTH 0x0B41 #define GL_CULL_FACE 0x0B44 #define GL_CULL_FACE_MODE 0x0B45 #define GL_FRONT_FACE 0x0B46 #define GL_DEPTH_RANGE 0x0B70 #define GL_DEPTH_TEST 0x0B71 #define GL_DEPTH_WRITEMASK 0x0B72 #define GL_DEPTH_CLEAR_VALUE 0x0B73 #define GL_DEPTH_FUNC 0x0B74 #define GL_STENCIL_TEST 0x0B90 #define GL_STENCIL_CLEAR_VALUE 0x0B91 #define GL_STENCIL_FUNC 0x0B92 #define GL_STENCIL_VALUE_MASK 0x0B93 #define GL_STENCIL_FAIL 0x0B94 #define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 #define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 #define GL_STENCIL_REF 0x0B97 #define GL_STENCIL_WRITEMASK 0x0B98 #define GL_VIEWPORT 0x0BA2 #define GL_DITHER 0x0BD0 #define GL_BLEND_DST 0x0BE0 #define GL_BLEND_SRC 0x0BE1 #define GL_BLEND 0x0BE2 #define GL_LOGIC_OP_MODE 0x0BF0 #define GL_DRAW_BUFFER 0x0C01 #define GL_READ_BUFFER 0x0C02 #define GL_SCISSOR_BOX 0x0C10 #define GL_SCISSOR_TEST 0x0C11 #define GL_COLOR_CLEAR_VALUE 0x0C22 #define GL_COLOR_WRITEMASK 0x0C23 #define GL_DOUBLEBUFFER 0x0C32 #define GL_STEREO 0x0C33 #define GL_LINE_SMOOTH_HINT 0x0C52 #define GL_POLYGON_SMOOTH_HINT 0x0C53 #define GL_UNPACK_SWAP_BYTES 0x0CF0 #define GL_UNPACK_LSB_FIRST 0x0CF1 #define GL_UNPACK_ROW_LENGTH 0x0CF2 #define GL_UNPACK_SKIP_ROWS 0x0CF3 #define GL_UNPACK_SKIP_PIXELS 0x0CF4 #define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_PACK_SWAP_BYTES 0x0D00 #define GL_PACK_LSB_FIRST 0x0D01 #define GL_PACK_ROW_LENGTH 0x0D02 #define GL_PACK_SKIP_ROWS 0x0D03 #define GL_PACK_SKIP_PIXELS 0x0D04 #define GL_PACK_ALIGNMENT 0x0D05 #define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_MAX_VIEWPORT_DIMS 0x0D3A #define GL_SUBPIXEL_BITS 0x0D50 #define GL_TEXTURE_1D 0x0DE0 #define GL_TEXTURE_2D 0x0DE1 #define GL_TEXTURE_WIDTH 0x1000 #define GL_TEXTURE_HEIGHT 0x1001 #define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_DONT_CARE 0x1100 #define GL_FASTEST 0x1101 #define GL_NICEST 0x1102 #define GL_BYTE 0x1400 #define GL_UNSIGNED_BYTE 0x1401 #define GL_SHORT 0x1402 #define GL_UNSIGNED_SHORT 0x1403 #define GL_INT 0x1404 #define GL_UNSIGNED_INT 0x1405 #define GL_FLOAT 0x1406 #define GL_CLEAR 0x1500 #define GL_AND 0x1501 #define GL_AND_REVERSE 0x1502 #define GL_COPY 0x1503 #define GL_AND_INVERTED 0x1504 #define GL_NOOP 0x1505 #define GL_XOR 0x1506 #define GL_OR 0x1507 #define GL_NOR 0x1508 #define GL_EQUIV 0x1509 #define GL_INVERT 0x150A #define GL_OR_REVERSE 0x150B #define GL_COPY_INVERTED 0x150C #define GL_OR_INVERTED 0x150D #define GL_NAND 0x150E #define GL_SET 0x150F #define GL_TEXTURE 0x1702 #define GL_COLOR 0x1800 #define GL_DEPTH 0x1801 #define GL_STENCIL 0x1802 #define GL_STENCIL_INDEX 0x1901 #define GL_DEPTH_COMPONENT 0x1902 #define GL_RED 0x1903 #define GL_GREEN 0x1904 #define GL_BLUE 0x1905 #define GL_ALPHA 0x1906 #define GL_RGB 0x1907 #define GL_RGBA 0x1908 #define GL_POINT 0x1B00 #define GL_LINE 0x1B01 #define GL_FILL 0x1B02 #define GL_KEEP 0x1E00 #define GL_REPLACE 0x1E01 #define GL_INCR 0x1E02 #define GL_DECR 0x1E03 #define GL_VENDOR 0x1F00 #define GL_RENDERER 0x1F01 #define GL_VERSION 0x1F02 #define GL_EXTENSIONS 0x1F03 #define GL_NEAREST 0x2600 #define GL_LINEAR 0x2601 #define GL_NEAREST_MIPMAP_NEAREST 0x2700 #define GL_LINEAR_MIPMAP_NEAREST 0x2701 #define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MIN_FILTER 0x2801 #define GL_TEXTURE_WRAP_S 0x2802 #define GL_TEXTURE_WRAP_T 0x2803 #define GL_REPEAT 0x2901 #define GL_COLOR_LOGIC_OP 0x0BF2 #define GL_POLYGON_OFFSET_UNITS 0x2A00 #define GL_POLYGON_OFFSET_POINT 0x2A01 #define GL_POLYGON_OFFSET_LINE 0x2A02 #define GL_POLYGON_OFFSET_FILL 0x8037 #define GL_POLYGON_OFFSET_FACTOR 0x8038 #define GL_TEXTURE_BINDING_1D 0x8068 #define GL_TEXTURE_BINDING_2D 0x8069 #define GL_TEXTURE_INTERNAL_FORMAT 0x1003 #define GL_TEXTURE_RED_SIZE 0x805C #define GL_TEXTURE_GREEN_SIZE 0x805D #define GL_TEXTURE_BLUE_SIZE 0x805E #define GL_TEXTURE_ALPHA_SIZE 0x805F #define GL_DOUBLE 0x140A #define GL_PROXY_TEXTURE_1D 0x8063 #define GL_PROXY_TEXTURE_2D 0x8064 #define GL_R3_G3_B2 0x2A10 #define GL_RGB4 0x804F #define GL_RGB5 0x8050 #define GL_RGB8 0x8051 #define GL_RGB10 0x8052 #define GL_RGB12 0x8053 #define GL_RGB16 0x8054 #define GL_RGBA2 0x8055 #define GL_RGBA4 0x8056 #define GL_RGB5_A1 0x8057 #define GL_RGBA8 0x8058 #define GL_RGB10_A2 0x8059 #define GL_RGBA12 0x805A #define GL_RGBA16 0x805B #define GL_UNSIGNED_BYTE_3_3_2 0x8032 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_INT_8_8_8_8 0x8035 #define GL_UNSIGNED_INT_10_10_10_2 0x8036 #define GL_TEXTURE_BINDING_3D 0x806A #define GL_PACK_SKIP_IMAGES 0x806B #define GL_PACK_IMAGE_HEIGHT 0x806C #define GL_UNPACK_SKIP_IMAGES 0x806D #define GL_UNPACK_IMAGE_HEIGHT 0x806E #define GL_TEXTURE_3D 0x806F #define GL_PROXY_TEXTURE_3D 0x8070 #define GL_TEXTURE_DEPTH 0x8071 #define GL_TEXTURE_WRAP_R 0x8072 #define GL_MAX_3D_TEXTURE_SIZE 0x8073 #define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 #define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 #define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #define GL_BGR 0x80E0 #define GL_BGRA 0x80E1 #define GL_MAX_ELEMENTS_VERTICES 0x80E8 #define GL_MAX_ELEMENTS_INDICES 0x80E9 #define GL_CLAMP_TO_EDGE 0x812F #define GL_TEXTURE_MIN_LOD 0x813A #define GL_TEXTURE_MAX_LOD 0x813B #define GL_TEXTURE_BASE_LEVEL 0x813C #define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 #define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 #define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 #define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E #define GL_TEXTURE0 0x84C0 #define GL_TEXTURE1 0x84C1 #define GL_TEXTURE2 0x84C2 #define GL_TEXTURE3 0x84C3 #define GL_TEXTURE4 0x84C4 #define GL_TEXTURE5 0x84C5 #define GL_TEXTURE6 0x84C6 #define GL_TEXTURE7 0x84C7 #define GL_TEXTURE8 0x84C8 #define GL_TEXTURE9 0x84C9 #define GL_TEXTURE10 0x84CA #define GL_TEXTURE11 0x84CB #define GL_TEXTURE12 0x84CC #define GL_TEXTURE13 0x84CD #define GL_TEXTURE14 0x84CE #define GL_TEXTURE15 0x84CF #define GL_TEXTURE16 0x84D0 #define GL_TEXTURE17 0x84D1 #define GL_TEXTURE18 0x84D2 #define GL_TEXTURE19 0x84D3 #define GL_TEXTURE20 0x84D4 #define GL_TEXTURE21 0x84D5 #define GL_TEXTURE22 0x84D6 #define GL_TEXTURE23 0x84D7 #define GL_TEXTURE24 0x84D8 #define GL_TEXTURE25 0x84D9 #define GL_TEXTURE26 0x84DA #define GL_TEXTURE27 0x84DB #define GL_TEXTURE28 0x84DC #define GL_TEXTURE29 0x84DD #define GL_TEXTURE30 0x84DE #define GL_TEXTURE31 0x84DF #define GL_ACTIVE_TEXTURE 0x84E0 #define GL_MULTISAMPLE 0x809D #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E #define GL_SAMPLE_ALPHA_TO_ONE 0x809F #define GL_SAMPLE_COVERAGE 0x80A0 #define GL_SAMPLE_BUFFERS 0x80A8 #define GL_SAMPLES 0x80A9 #define GL_SAMPLE_COVERAGE_VALUE 0x80AA #define GL_SAMPLE_COVERAGE_INVERT 0x80AB #define GL_TEXTURE_CUBE_MAP 0x8513 #define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A #define GL_PROXY_TEXTURE_CUBE_MAP 0x851B #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C #define GL_COMPRESSED_RGB 0x84ED #define GL_COMPRESSED_RGBA 0x84EE #define GL_TEXTURE_COMPRESSION_HINT 0x84EF #define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 #define GL_TEXTURE_COMPRESSED 0x86A1 #define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 #define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 #define GL_CLAMP_TO_BORDER 0x812D #define GL_BLEND_DST_RGB 0x80C8 #define GL_BLEND_SRC_RGB 0x80C9 #define GL_BLEND_DST_ALPHA 0x80CA #define GL_BLEND_SRC_ALPHA 0x80CB #define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_DEPTH_COMPONENT24 0x81A6 #define GL_DEPTH_COMPONENT32 0x81A7 #define GL_MIRRORED_REPEAT 0x8370 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD #define GL_TEXTURE_LOD_BIAS 0x8501 #define GL_INCR_WRAP 0x8507 #define GL_DECR_WRAP 0x8508 #define GL_TEXTURE_DEPTH_SIZE 0x884A #define GL_TEXTURE_COMPARE_MODE 0x884C #define GL_TEXTURE_COMPARE_FUNC 0x884D #define GL_BLEND_COLOR 0x8005 #define GL_BLEND_EQUATION 0x8009 #define GL_CONSTANT_COLOR 0x8001 #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 #define GL_CONSTANT_ALPHA 0x8003 #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 #define GL_FUNC_ADD 0x8006 #define GL_FUNC_REVERSE_SUBTRACT 0x800B #define GL_FUNC_SUBTRACT 0x800A #define GL_MIN 0x8007 #define GL_MAX 0x8008 #define GL_BUFFER_SIZE 0x8764 #define GL_BUFFER_USAGE 0x8765 #define GL_QUERY_COUNTER_BITS 0x8864 #define GL_CURRENT_QUERY 0x8865 #define GL_QUERY_RESULT 0x8866 #define GL_QUERY_RESULT_AVAILABLE 0x8867 #define GL_ARRAY_BUFFER 0x8892 #define GL_ELEMENT_ARRAY_BUFFER 0x8893 #define GL_ARRAY_BUFFER_BINDING 0x8894 #define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F #define GL_READ_ONLY 0x88B8 #define GL_WRITE_ONLY 0x88B9 #define GL_READ_WRITE 0x88BA #define GL_BUFFER_ACCESS 0x88BB #define GL_BUFFER_MAPPED 0x88BC #define GL_BUFFER_MAP_POINTER 0x88BD #define GL_STREAM_DRAW 0x88E0 #define GL_STREAM_READ 0x88E1 #define GL_STREAM_COPY 0x88E2 #define GL_STATIC_DRAW 0x88E4 #define GL_STATIC_READ 0x88E5 #define GL_STATIC_COPY 0x88E6 #define GL_DYNAMIC_DRAW 0x88E8 #define GL_DYNAMIC_READ 0x88E9 #define GL_DYNAMIC_COPY 0x88EA #define GL_SAMPLES_PASSED 0x8914 #define GL_SRC1_ALPHA 0x8589 #define GL_BLEND_EQUATION_RGB 0x8009 #define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 #define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 #define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 #define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 #define GL_CURRENT_VERTEX_ATTRIB 0x8626 #define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 #define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 #define GL_STENCIL_BACK_FUNC 0x8800 #define GL_STENCIL_BACK_FAIL 0x8801 #define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 #define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 #define GL_MAX_DRAW_BUFFERS 0x8824 #define GL_DRAW_BUFFER0 0x8825 #define GL_DRAW_BUFFER1 0x8826 #define GL_DRAW_BUFFER2 0x8827 #define GL_DRAW_BUFFER3 0x8828 #define GL_DRAW_BUFFER4 0x8829 #define GL_DRAW_BUFFER5 0x882A #define GL_DRAW_BUFFER6 0x882B #define GL_DRAW_BUFFER7 0x882C #define GL_DRAW_BUFFER8 0x882D #define GL_DRAW_BUFFER9 0x882E #define GL_DRAW_BUFFER10 0x882F #define GL_DRAW_BUFFER11 0x8830 #define GL_DRAW_BUFFER12 0x8831 #define GL_DRAW_BUFFER13 0x8832 #define GL_DRAW_BUFFER14 0x8833 #define GL_DRAW_BUFFER15 0x8834 #define GL_BLEND_EQUATION_ALPHA 0x883D #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 #define GL_FRAGMENT_SHADER 0x8B30 #define GL_VERTEX_SHADER 0x8B31 #define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A #define GL_MAX_VARYING_FLOATS 0x8B4B #define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D #define GL_SHADER_TYPE 0x8B4F #define GL_FLOAT_VEC2 0x8B50 #define GL_FLOAT_VEC3 0x8B51 #define GL_FLOAT_VEC4 0x8B52 #define GL_INT_VEC2 0x8B53 #define GL_INT_VEC3 0x8B54 #define GL_INT_VEC4 0x8B55 #define GL_BOOL 0x8B56 #define GL_BOOL_VEC2 0x8B57 #define GL_BOOL_VEC3 0x8B58 #define GL_BOOL_VEC4 0x8B59 #define GL_FLOAT_MAT2 0x8B5A #define GL_FLOAT_MAT3 0x8B5B #define GL_FLOAT_MAT4 0x8B5C #define GL_SAMPLER_1D 0x8B5D #define GL_SAMPLER_2D 0x8B5E #define GL_SAMPLER_3D 0x8B5F #define GL_SAMPLER_CUBE 0x8B60 #define GL_SAMPLER_1D_SHADOW 0x8B61 #define GL_SAMPLER_2D_SHADOW 0x8B62 #define GL_DELETE_STATUS 0x8B80 #define GL_COMPILE_STATUS 0x8B81 #define GL_LINK_STATUS 0x8B82 #define GL_VALIDATE_STATUS 0x8B83 #define GL_INFO_LOG_LENGTH 0x8B84 #define GL_ATTACHED_SHADERS 0x8B85 #define GL_ACTIVE_UNIFORMS 0x8B86 #define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 #define GL_SHADER_SOURCE_LENGTH 0x8B88 #define GL_ACTIVE_ATTRIBUTES 0x8B89 #define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B #define GL_SHADING_LANGUAGE_VERSION 0x8B8C #define GL_CURRENT_PROGRAM 0x8B8D #define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 #define GL_LOWER_LEFT 0x8CA1 #define GL_UPPER_LEFT 0x8CA2 #define GL_STENCIL_BACK_REF 0x8CA3 #define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 #define GL_STENCIL_BACK_WRITEMASK 0x8CA5 #define GL_PIXEL_PACK_BUFFER 0x88EB #define GL_PIXEL_UNPACK_BUFFER 0x88EC #define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED #define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF #define GL_FLOAT_MAT2x3 0x8B65 #define GL_FLOAT_MAT2x4 0x8B66 #define GL_FLOAT_MAT3x2 0x8B67 #define GL_FLOAT_MAT3x4 0x8B68 #define GL_FLOAT_MAT4x2 0x8B69 #define GL_FLOAT_MAT4x3 0x8B6A #define GL_SRGB 0x8C40 #define GL_SRGB8 0x8C41 #define GL_SRGB_ALPHA 0x8C42 #define GL_SRGB8_ALPHA8 0x8C43 #define GL_COMPRESSED_SRGB 0x8C48 #define GL_COMPRESSED_SRGB_ALPHA 0x8C49 #define GL_COMPARE_REF_TO_TEXTURE 0x884E #define GL_CLIP_DISTANCE0 0x3000 #define GL_CLIP_DISTANCE1 0x3001 #define GL_CLIP_DISTANCE2 0x3002 #define GL_CLIP_DISTANCE3 0x3003 #define GL_CLIP_DISTANCE4 0x3004 #define GL_CLIP_DISTANCE5 0x3005 #define GL_CLIP_DISTANCE6 0x3006 #define GL_CLIP_DISTANCE7 0x3007 #define GL_MAX_CLIP_DISTANCES 0x0D32 #define GL_MAJOR_VERSION 0x821B #define GL_MINOR_VERSION 0x821C #define GL_NUM_EXTENSIONS 0x821D #define GL_CONTEXT_FLAGS 0x821E #define GL_COMPRESSED_RED 0x8225 #define GL_COMPRESSED_RG 0x8226 #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 #define GL_RGBA32F 0x8814 #define GL_RGB32F 0x8815 #define GL_RGBA16F 0x881A #define GL_RGB16F 0x881B #define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF #define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 #define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 #define GL_CLAMP_READ_COLOR 0x891C #define GL_FIXED_ONLY 0x891D #define GL_MAX_VARYING_COMPONENTS 0x8B4B #define GL_TEXTURE_1D_ARRAY 0x8C18 #define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 #define GL_TEXTURE_2D_ARRAY 0x8C1A #define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B #define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C #define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D #define GL_R11F_G11F_B10F 0x8C3A #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B #define GL_RGB9_E5 0x8C3D #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E #define GL_TEXTURE_SHARED_SIZE 0x8C3F #define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 #define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 #define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 #define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 #define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 #define GL_PRIMITIVES_GENERATED 0x8C87 #define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 #define GL_RASTERIZER_DISCARD 0x8C89 #define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B #define GL_INTERLEAVED_ATTRIBS 0x8C8C #define GL_SEPARATE_ATTRIBS 0x8C8D #define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E #define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F #define GL_RGBA32UI 0x8D70 #define GL_RGB32UI 0x8D71 #define GL_RGBA16UI 0x8D76 #define GL_RGB16UI 0x8D77 #define GL_RGBA8UI 0x8D7C #define GL_RGB8UI 0x8D7D #define GL_RGBA32I 0x8D82 #define GL_RGB32I 0x8D83 #define GL_RGBA16I 0x8D88 #define GL_RGB16I 0x8D89 #define GL_RGBA8I 0x8D8E #define GL_RGB8I 0x8D8F #define GL_RED_INTEGER 0x8D94 #define GL_GREEN_INTEGER 0x8D95 #define GL_BLUE_INTEGER 0x8D96 #define GL_RGB_INTEGER 0x8D98 #define GL_RGBA_INTEGER 0x8D99 #define GL_BGR_INTEGER 0x8D9A #define GL_BGRA_INTEGER 0x8D9B #define GL_SAMPLER_1D_ARRAY 0x8DC0 #define GL_SAMPLER_2D_ARRAY 0x8DC1 #define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 #define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 #define GL_SAMPLER_CUBE_SHADOW 0x8DC5 #define GL_UNSIGNED_INT_VEC2 0x8DC6 #define GL_UNSIGNED_INT_VEC3 0x8DC7 #define GL_UNSIGNED_INT_VEC4 0x8DC8 #define GL_INT_SAMPLER_1D 0x8DC9 #define GL_INT_SAMPLER_2D 0x8DCA #define GL_INT_SAMPLER_3D 0x8DCB #define GL_INT_SAMPLER_CUBE 0x8DCC #define GL_INT_SAMPLER_1D_ARRAY 0x8DCE #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF #define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 #define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 #define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 #define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 #define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 #define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 #define GL_QUERY_WAIT 0x8E13 #define GL_QUERY_NO_WAIT 0x8E14 #define GL_QUERY_BY_REGION_WAIT 0x8E15 #define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 #define GL_BUFFER_ACCESS_FLAGS 0x911F #define GL_BUFFER_MAP_LENGTH 0x9120 #define GL_BUFFER_MAP_OFFSET 0x9121 #define GL_DEPTH_COMPONENT32F 0x8CAC #define GL_DEPTH32F_STENCIL8 0x8CAD #define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 #define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 #define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 #define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 #define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 #define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 #define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 #define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 #define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 #define GL_FRAMEBUFFER_DEFAULT 0x8218 #define GL_FRAMEBUFFER_UNDEFINED 0x8219 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A #define GL_MAX_RENDERBUFFER_SIZE 0x84E8 #define GL_DEPTH_STENCIL 0x84F9 #define GL_UNSIGNED_INT_24_8 0x84FA #define GL_DEPTH24_STENCIL8 0x88F0 #define GL_TEXTURE_STENCIL_SIZE 0x88F1 #define GL_TEXTURE_RED_TYPE 0x8C10 #define GL_TEXTURE_GREEN_TYPE 0x8C11 #define GL_TEXTURE_BLUE_TYPE 0x8C12 #define GL_TEXTURE_ALPHA_TYPE 0x8C13 #define GL_TEXTURE_DEPTH_TYPE 0x8C16 #define GL_UNSIGNED_NORMALIZED 0x8C17 #define GL_FRAMEBUFFER_BINDING 0x8CA6 #define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 #define GL_RENDERBUFFER_BINDING 0x8CA7 #define GL_READ_FRAMEBUFFER 0x8CA8 #define GL_DRAW_FRAMEBUFFER 0x8CA9 #define GL_READ_FRAMEBUFFER_BINDING 0x8CAA #define GL_RENDERBUFFER_SAMPLES 0x8CAB #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD #define GL_MAX_COLOR_ATTACHMENTS 0x8CDF #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_COLOR_ATTACHMENT1 0x8CE1 #define GL_COLOR_ATTACHMENT2 0x8CE2 #define GL_COLOR_ATTACHMENT3 0x8CE3 #define GL_COLOR_ATTACHMENT4 0x8CE4 #define GL_COLOR_ATTACHMENT5 0x8CE5 #define GL_COLOR_ATTACHMENT6 0x8CE6 #define GL_COLOR_ATTACHMENT7 0x8CE7 #define GL_COLOR_ATTACHMENT8 0x8CE8 #define GL_COLOR_ATTACHMENT9 0x8CE9 #define GL_COLOR_ATTACHMENT10 0x8CEA #define GL_COLOR_ATTACHMENT11 0x8CEB #define GL_COLOR_ATTACHMENT12 0x8CEC #define GL_COLOR_ATTACHMENT13 0x8CED #define GL_COLOR_ATTACHMENT14 0x8CEE #define GL_COLOR_ATTACHMENT15 0x8CEF #define GL_COLOR_ATTACHMENT16 0x8CF0 #define GL_COLOR_ATTACHMENT17 0x8CF1 #define GL_COLOR_ATTACHMENT18 0x8CF2 #define GL_COLOR_ATTACHMENT19 0x8CF3 #define GL_COLOR_ATTACHMENT20 0x8CF4 #define GL_COLOR_ATTACHMENT21 0x8CF5 #define GL_COLOR_ATTACHMENT22 0x8CF6 #define GL_COLOR_ATTACHMENT23 0x8CF7 #define GL_COLOR_ATTACHMENT24 0x8CF8 #define GL_COLOR_ATTACHMENT25 0x8CF9 #define GL_COLOR_ATTACHMENT26 0x8CFA #define GL_COLOR_ATTACHMENT27 0x8CFB #define GL_COLOR_ATTACHMENT28 0x8CFC #define GL_COLOR_ATTACHMENT29 0x8CFD #define GL_COLOR_ATTACHMENT30 0x8CFE #define GL_COLOR_ATTACHMENT31 0x8CFF #define GL_DEPTH_ATTACHMENT 0x8D00 #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_FRAMEBUFFER 0x8D40 #define GL_RENDERBUFFER 0x8D41 #define GL_RENDERBUFFER_WIDTH 0x8D42 #define GL_RENDERBUFFER_HEIGHT 0x8D43 #define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 #define GL_STENCIL_INDEX1 0x8D46 #define GL_STENCIL_INDEX4 0x8D47 #define GL_STENCIL_INDEX8 0x8D48 #define GL_STENCIL_INDEX16 0x8D49 #define GL_RENDERBUFFER_RED_SIZE 0x8D50 #define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 #define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 #define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 #define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 #define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #define GL_MAX_SAMPLES 0x8D57 #define GL_FRAMEBUFFER_SRGB 0x8DB9 #define GL_HALF_FLOAT 0x140B #define GL_MAP_READ_BIT 0x0001 #define GL_MAP_WRITE_BIT 0x0002 #define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 #define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 #define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 #define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 #define GL_COMPRESSED_RED_RGTC1 0x8DBB #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC #define GL_COMPRESSED_RG_RGTC2 0x8DBD #define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE #define GL_RG 0x8227 #define GL_RG_INTEGER 0x8228 #define GL_R8 0x8229 #define GL_R16 0x822A #define GL_RG8 0x822B #define GL_RG16 0x822C #define GL_R16F 0x822D #define GL_R32F 0x822E #define GL_RG16F 0x822F #define GL_RG32F 0x8230 #define GL_R8I 0x8231 #define GL_R8UI 0x8232 #define GL_R16I 0x8233 #define GL_R16UI 0x8234 #define GL_R32I 0x8235 #define GL_R32UI 0x8236 #define GL_RG8I 0x8237 #define GL_RG8UI 0x8238 #define GL_RG16I 0x8239 #define GL_RG16UI 0x823A #define GL_RG32I 0x823B #define GL_RG32UI 0x823C #define GL_VERTEX_ARRAY_BINDING 0x85B5 #define GL_SAMPLER_2D_RECT 0x8B63 #define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 #define GL_SAMPLER_BUFFER 0x8DC2 #define GL_INT_SAMPLER_2D_RECT 0x8DCD #define GL_INT_SAMPLER_BUFFER 0x8DD0 #define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 #define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 #define GL_TEXTURE_BUFFER 0x8C2A #define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B #define GL_TEXTURE_BINDING_BUFFER 0x8C2C #define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D #define GL_TEXTURE_RECTANGLE 0x84F5 #define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 #define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 #define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 #define GL_R8_SNORM 0x8F94 #define GL_RG8_SNORM 0x8F95 #define GL_RGB8_SNORM 0x8F96 #define GL_RGBA8_SNORM 0x8F97 #define GL_R16_SNORM 0x8F98 #define GL_RG16_SNORM 0x8F99 #define GL_RGB16_SNORM 0x8F9A #define GL_RGBA16_SNORM 0x8F9B #define GL_SIGNED_NORMALIZED 0x8F9C #define GL_PRIMITIVE_RESTART 0x8F9D #define GL_PRIMITIVE_RESTART_INDEX 0x8F9E #define GL_COPY_READ_BUFFER 0x8F36 #define GL_COPY_WRITE_BUFFER 0x8F37 #define GL_UNIFORM_BUFFER 0x8A11 #define GL_UNIFORM_BUFFER_BINDING 0x8A28 #define GL_UNIFORM_BUFFER_START 0x8A29 #define GL_UNIFORM_BUFFER_SIZE 0x8A2A #define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B #define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C #define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D #define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E #define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F #define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 #define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 #define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 #define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 #define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 #define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 #define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 #define GL_UNIFORM_TYPE 0x8A37 #define GL_UNIFORM_SIZE 0x8A38 #define GL_UNIFORM_NAME_LENGTH 0x8A39 #define GL_UNIFORM_BLOCK_INDEX 0x8A3A #define GL_UNIFORM_OFFSET 0x8A3B #define GL_UNIFORM_ARRAY_STRIDE 0x8A3C #define GL_UNIFORM_MATRIX_STRIDE 0x8A3D #define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E #define GL_UNIFORM_BLOCK_BINDING 0x8A3F #define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 #define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 #define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 #define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 #define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 #define GL_INVALID_INDEX 0xFFFFFFFF #define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 #define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 #define GL_LINES_ADJACENCY 0x000A #define GL_LINE_STRIP_ADJACENCY 0x000B #define GL_TRIANGLES_ADJACENCY 0x000C #define GL_TRIANGLE_STRIP_ADJACENCY 0x000D #define GL_PROGRAM_POINT_SIZE 0x8642 #define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 #define GL_GEOMETRY_SHADER 0x8DD9 #define GL_GEOMETRY_VERTICES_OUT 0x8916 #define GL_GEOMETRY_INPUT_TYPE 0x8917 #define GL_GEOMETRY_OUTPUT_TYPE 0x8918 #define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF #define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 #define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 #define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 #define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 #define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 #define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 #define GL_CONTEXT_PROFILE_MASK 0x9126 #define GL_DEPTH_CLAMP 0x864F #define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C #define GL_FIRST_VERTEX_CONVENTION 0x8E4D #define GL_LAST_VERTEX_CONVENTION 0x8E4E #define GL_PROVOKING_VERTEX 0x8E4F #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F #define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 #define GL_OBJECT_TYPE 0x9112 #define GL_SYNC_CONDITION 0x9113 #define GL_SYNC_STATUS 0x9114 #define GL_SYNC_FLAGS 0x9115 #define GL_SYNC_FENCE 0x9116 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 #define GL_UNSIGNALED 0x9118 #define GL_SIGNALED 0x9119 #define GL_ALREADY_SIGNALED 0x911A #define GL_TIMEOUT_EXPIRED 0x911B #define GL_CONDITION_SATISFIED 0x911C #define GL_WAIT_FAILED 0x911D #define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF #define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 #define GL_SAMPLE_POSITION 0x8E50 #define GL_SAMPLE_MASK 0x8E51 #define GL_SAMPLE_MASK_VALUE 0x8E52 #define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 #define GL_TEXTURE_2D_MULTISAMPLE 0x9100 #define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 #define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 #define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 #define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 #define GL_TEXTURE_SAMPLES 0x9106 #define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 #define GL_SAMPLER_2D_MULTISAMPLE 0x9108 #define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 #define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A #define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B #define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C #define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D #define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E #define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F #define GL_MAX_INTEGER_SAMPLES 0x9110 #define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE #define GL_SRC1_COLOR 0x88F9 #define GL_ONE_MINUS_SRC1_COLOR 0x88FA #define GL_ONE_MINUS_SRC1_ALPHA 0x88FB #define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC #define GL_ANY_SAMPLES_PASSED 0x8C2F #define GL_SAMPLER_BINDING 0x8919 #define GL_RGB10_A2UI 0x906F #define GL_TEXTURE_SWIZZLE_R 0x8E42 #define GL_TEXTURE_SWIZZLE_G 0x8E43 #define GL_TEXTURE_SWIZZLE_B 0x8E44 #define GL_TEXTURE_SWIZZLE_A 0x8E45 #define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 #define GL_TIME_ELAPSED 0x88BF #define GL_TIMESTAMP 0x8E28 #define GL_INT_2_10_10_10_REV 0x8D9F #ifndef GL_VERSION_1_0 #define GL_VERSION_1_0 1 GLAPI int GLAD_GL_VERSION_1_0; typedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode); GLAPI PFNGLCULLFACEPROC glad_glCullFace; #define glCullFace glad_glCullFace typedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode); GLAPI PFNGLFRONTFACEPROC glad_glFrontFace; #define glFrontFace glad_glFrontFace typedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode); GLAPI PFNGLHINTPROC glad_glHint; #define glHint glad_glHint typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width); GLAPI PFNGLLINEWIDTHPROC glad_glLineWidth; #define glLineWidth glad_glLineWidth typedef void (APIENTRYP PFNGLPOINTSIZEPROC)(GLfloat size); GLAPI PFNGLPOINTSIZEPROC glad_glPointSize; #define glPointSize glad_glPointSize typedef void (APIENTRYP PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); GLAPI PFNGLPOLYGONMODEPROC glad_glPolygonMode; #define glPolygonMode glad_glPolygonMode typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLSCISSORPROC glad_glScissor; #define glScissor glad_glScissor typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); GLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf; #define glTexParameterf glad_glTexParameterf typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params); GLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; #define glTexParameterfv glad_glTexParameterfv typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); GLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri; #define glTexParameteri glad_glTexParameteri typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params); GLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; #define glTexParameteriv glad_glTexParameteriv typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE1DPROC glad_glTexImage1D; #define glTexImage1D glad_glTexImage1D typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D; #define glTexImage2D glad_glTexImage2D typedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum buf); GLAPI PFNGLDRAWBUFFERPROC glad_glDrawBuffer; #define glDrawBuffer glad_glDrawBuffer typedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask); GLAPI PFNGLCLEARPROC glad_glClear; #define glClear glad_glClear typedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); GLAPI PFNGLCLEARCOLORPROC glad_glClearColor; #define glClearColor glad_glClearColor typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s); GLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil; #define glClearStencil glad_glClearStencil typedef void (APIENTRYP PFNGLCLEARDEPTHPROC)(GLdouble depth); GLAPI PFNGLCLEARDEPTHPROC glad_glClearDepth; #define glClearDepth glad_glClearDepth typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask); GLAPI PFNGLSTENCILMASKPROC glad_glStencilMask; #define glStencilMask glad_glStencilMask typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); GLAPI PFNGLCOLORMASKPROC glad_glColorMask; #define glColorMask glad_glColorMask typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag); GLAPI PFNGLDEPTHMASKPROC glad_glDepthMask; #define glDepthMask glad_glDepthMask typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap); GLAPI PFNGLDISABLEPROC glad_glDisable; #define glDisable glad_glDisable typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap); GLAPI PFNGLENABLEPROC glad_glEnable; #define glEnable glad_glEnable typedef void (APIENTRYP PFNGLFINISHPROC)(void); GLAPI PFNGLFINISHPROC glad_glFinish; #define glFinish glad_glFinish typedef void (APIENTRYP PFNGLFLUSHPROC)(void); GLAPI PFNGLFLUSHPROC glad_glFlush; #define glFlush glad_glFlush typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); GLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc; #define glBlendFunc glad_glBlendFunc typedef void (APIENTRYP PFNGLLOGICOPPROC)(GLenum opcode); GLAPI PFNGLLOGICOPPROC glad_glLogicOp; #define glLogicOp glad_glLogicOp typedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); GLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc; #define glStencilFunc glad_glStencilFunc typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); GLAPI PFNGLSTENCILOPPROC glad_glStencilOp; #define glStencilOp glad_glStencilOp typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func); GLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc; #define glDepthFunc glad_glDepthFunc typedef void (APIENTRYP PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); GLAPI PFNGLPIXELSTOREFPROC glad_glPixelStoref; #define glPixelStoref glad_glPixelStoref typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei; #define glPixelStorei glad_glPixelStorei typedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum src); GLAPI PFNGLREADBUFFERPROC glad_glReadBuffer; #define glReadBuffer glad_glReadBuffer typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); GLAPI PFNGLREADPIXELSPROC glad_glReadPixels; #define glReadPixels glad_glReadPixels typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data); GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv; #define glGetBooleanv glad_glGetBooleanv typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data); GLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev; #define glGetDoublev glad_glGetDoublev typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void); GLAPI PFNGLGETERRORPROC glad_glGetError; #define glGetError glad_glGetError typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data); GLAPI PFNGLGETFLOATVPROC glad_glGetFloatv; #define glGetFloatv glad_glGetFloatv typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data); GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv; #define glGetIntegerv glad_glGetIntegerv typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name); GLAPI PFNGLGETSTRINGPROC glad_glGetString; #define glGetString glad_glGetString typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void *pixels); GLAPI PFNGLGETTEXIMAGEPROC glad_glGetTexImage; #define glGetTexImage glad_glGetTexImage typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params); GLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; #define glGetTexParameterfv glad_glGetTexParameterfv typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; #define glGetTexParameteriv glad_glGetTexParameteriv typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat *params); GLAPI PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv; #define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint *params); GLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv; #define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap); GLAPI PFNGLISENABLEDPROC glad_glIsEnabled; #define glIsEnabled glad_glIsEnabled typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); GLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange; #define glDepthRange glad_glDepthRange typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLVIEWPORTPROC glad_glViewport; #define glViewport glad_glViewport #endif #ifndef GL_VERSION_1_1 #define GL_VERSION_1_1 1 GLAPI int GLAD_GL_VERSION_1_1; typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); GLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays; #define glDrawArrays glad_glDrawArrays typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices); GLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements; #define glDrawElements glad_glDrawElements typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); GLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; #define glPolygonOffset glad_glPolygonOffset typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); GLAPI PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D; #define glCopyTexImage1D glad_glCopyTexImage1D typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); GLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; #define glCopyTexImage2D glad_glCopyTexImage2D typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); GLAPI PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D; #define glCopyTexSubImage1D glad_glCopyTexSubImage1D typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; #define glCopyTexSubImage2D glad_glCopyTexSubImage2D typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D; #define glTexSubImage1D glad_glTexSubImage1D typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; #define glTexSubImage2D glad_glTexSubImage2D typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture; #define glBindTexture glad_glBindTexture typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures); GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures; #define glDeleteTextures glad_glDeleteTextures typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures); GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures; #define glGenTextures glad_glGenTextures typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture); GLAPI PFNGLISTEXTUREPROC glad_glIsTexture; #define glIsTexture glad_glIsTexture #endif #ifndef GL_VERSION_1_2 #define GL_VERSION_1_2 1 GLAPI int GLAD_GL_VERSION_1_2; typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); GLAPI PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; #define glDrawRangeElements glad_glDrawRangeElements typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE3DPROC glad_glTexImage3D; #define glTexImage3D glad_glTexImage3D typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D; #define glTexSubImage3D glad_glTexSubImage3D typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; #define glCopyTexSubImage3D glad_glCopyTexSubImage3D #endif #ifndef GL_VERSION_1_3 #define GL_VERSION_1_3 1 GLAPI int GLAD_GL_VERSION_1_3; typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture); GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture; #define glActiveTexture glad_glActiveTexture typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; #define glSampleCoverage glad_glSampleCoverage typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; #define glCompressedTexImage3D glad_glCompressedTexImage3D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; #define glCompressedTexImage2D glad_glCompressedTexImage2D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D; #define glCompressedTexImage1D glad_glCompressedTexImage1D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D; #define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; #define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D; #define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void *img); GLAPI PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage; #define glGetCompressedTexImage glad_glGetCompressedTexImage #endif #ifndef GL_VERSION_1_4 #define GL_VERSION_1_4 1 GLAPI int GLAD_GL_VERSION_1_4; typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; #define glBlendFuncSeparate glad_glBlendFuncSeparate typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); GLAPI PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays; #define glMultiDrawArrays glad_glMultiDrawArrays typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); GLAPI PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; #define glMultiDrawElements glad_glMultiDrawElements typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); GLAPI PFNGLPOINTPARAMETERFPROC glad_glPointParameterf; #define glPointParameterf glad_glPointParameterf typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat *params); GLAPI PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv; #define glPointParameterfv glad_glPointParameterfv typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); GLAPI PFNGLPOINTPARAMETERIPROC glad_glPointParameteri; #define glPointParameteri glad_glPointParameteri typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint *params); GLAPI PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv; #define glPointParameteriv glad_glPointParameteriv typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); GLAPI PFNGLBLENDCOLORPROC glad_glBlendColor; #define glBlendColor glad_glBlendColor typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode); GLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation; #define glBlendEquation glad_glBlendEquation #endif #ifndef GL_VERSION_1_5 #define GL_VERSION_1_5 1 GLAPI int GLAD_GL_VERSION_1_5; typedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei n, GLuint *ids); GLAPI PFNGLGENQUERIESPROC glad_glGenQueries; #define glGenQueries glad_glGenQueries typedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint *ids); GLAPI PFNGLDELETEQUERIESPROC glad_glDeleteQueries; #define glDeleteQueries glad_glDeleteQueries typedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint id); GLAPI PFNGLISQUERYPROC glad_glIsQuery; #define glIsQuery glad_glIsQuery typedef void (APIENTRYP PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); GLAPI PFNGLBEGINQUERYPROC glad_glBeginQuery; #define glBeginQuery glad_glBeginQuery typedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum target); GLAPI PFNGLENDQUERYPROC glad_glEndQuery; #define glEndQuery glad_glEndQuery typedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETQUERYIVPROC glad_glGetQueryiv; #define glGetQueryiv glad_glGetQueryiv typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint *params); GLAPI PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv; #define glGetQueryObjectiv glad_glGetQueryObjectiv typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params); GLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; #define glGetQueryObjectuiv glad_glGetQueryObjectuiv typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer; #define glBindBuffer glad_glBindBuffer typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers); GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; #define glDeleteBuffers glad_glDeleteBuffers typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers); GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers; #define glGenBuffers glad_glGenBuffers typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer); GLAPI PFNGLISBUFFERPROC glad_glIsBuffer; #define glIsBuffer glad_glIsBuffer typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage); GLAPI PFNGLBUFFERDATAPROC glad_glBufferData; #define glBufferData glad_glBufferData typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); GLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; #define glBufferSubData glad_glBufferSubData typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void *data); GLAPI PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData; #define glGetBufferSubData glad_glGetBufferSubData typedef void * (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); GLAPI PFNGLMAPBUFFERPROC glad_glMapBuffer; #define glMapBuffer glad_glMapBuffer typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum target); GLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; #define glUnmapBuffer glad_glUnmapBuffer typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; #define glGetBufferParameteriv glad_glGetBufferParameteriv typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params); GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; #define glGetBufferPointerv glad_glGetBufferPointerv #endif #ifndef GL_VERSION_2_0 #define GL_VERSION_2_0 1 GLAPI int GLAD_GL_VERSION_2_0; typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; #define glBlendEquationSeparate glad_glBlendEquationSeparate typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs); GLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; #define glDrawBuffers glad_glDrawBuffers typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); GLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; #define glStencilOpSeparate glad_glStencilOpSeparate typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); GLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; #define glStencilFuncSeparate glad_glStencilFuncSeparate typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; #define glStencilMaskSeparate glad_glStencilMaskSeparate typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); GLAPI PFNGLATTACHSHADERPROC glad_glAttachShader; #define glAttachShader glad_glAttachShader typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name); GLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; #define glBindAttribLocation glad_glBindAttribLocation typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader); GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader; #define glCompileShader glad_glCompileShader typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void); GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram; #define glCreateProgram glad_glCreateProgram typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type); GLAPI PFNGLCREATESHADERPROC glad_glCreateShader; #define glCreateShader glad_glCreateShader typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program); GLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; #define glDeleteProgram glad_glDeleteProgram typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader); GLAPI PFNGLDELETESHADERPROC glad_glDeleteShader; #define glDeleteShader glad_glDeleteShader typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); GLAPI PFNGLDETACHSHADERPROC glad_glDetachShader; #define glDetachShader glad_glDetachShader typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; #define glDisableVertexAttribArray glad_glDisableVertexAttribArray typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; #define glEnableVertexAttribArray glad_glEnableVertexAttribArray typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); GLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; #define glGetActiveAttrib glad_glGetActiveAttrib typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); GLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; #define glGetActiveUniform glad_glGetActiveUniform typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); GLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; #define glGetAttachedShaders glad_glGetAttachedShaders typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; #define glGetAttribLocation glad_glGetAttribLocation typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params); GLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; #define glGetProgramiv glad_glGetProgramiv typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; #define glGetProgramInfoLog glad_glGetProgramInfoLog typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params); GLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv; #define glGetShaderiv glad_glGetShaderiv typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; #define glGetShaderInfoLog glad_glGetShaderInfoLog typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; #define glGetShaderSource glad_glGetShaderSource typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; #define glGetUniformLocation glad_glGetUniformLocation typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params); GLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; #define glGetUniformfv glad_glGetUniformfv typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params); GLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; #define glGetUniformiv glad_glGetUniformiv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble *params); GLAPI PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv; #define glGetVertexAttribdv glad_glGetVertexAttribdv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params); GLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; #define glGetVertexAttribfv glad_glGetVertexAttribfv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params); GLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; #define glGetVertexAttribiv glad_glGetVertexAttribiv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer); GLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; #define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program); GLAPI PFNGLISPROGRAMPROC glad_glIsProgram; #define glIsProgram glad_glIsProgram typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader); GLAPI PFNGLISSHADERPROC glad_glIsShader; #define glIsShader glad_glIsShader typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program); GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram; #define glLinkProgram glad_glLinkProgram typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); GLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource; #define glShaderSource glad_glShaderSource typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program); GLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram; #define glUseProgram glad_glUseProgram typedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); GLAPI PFNGLUNIFORM1FPROC glad_glUniform1f; #define glUniform1f glad_glUniform1f typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); GLAPI PFNGLUNIFORM2FPROC glad_glUniform2f; #define glUniform2f glad_glUniform2f typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); GLAPI PFNGLUNIFORM3FPROC glad_glUniform3f; #define glUniform3f glad_glUniform3f typedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); GLAPI PFNGLUNIFORM4FPROC glad_glUniform4f; #define glUniform4f glad_glUniform4f typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0); GLAPI PFNGLUNIFORM1IPROC glad_glUniform1i; #define glUniform1i glad_glUniform1i typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); GLAPI PFNGLUNIFORM2IPROC glad_glUniform2i; #define glUniform2i glad_glUniform2i typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i; #define glUniform3i glad_glUniform3i typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); GLAPI PFNGLUNIFORM4IPROC glad_glUniform4i; #define glUniform4i glad_glUniform4i typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv; #define glUniform1fv glad_glUniform1fv typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv; #define glUniform2fv glad_glUniform2fv typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv; #define glUniform3fv glad_glUniform3fv typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv; #define glUniform4fv glad_glUniform4fv typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv; #define glUniform1iv glad_glUniform1iv typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv; #define glUniform2iv glad_glUniform2iv typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv; #define glUniform3iv glad_glUniform3iv typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv; #define glUniform4iv glad_glUniform4iv typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; #define glUniformMatrix2fv glad_glUniformMatrix2fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; #define glUniformMatrix3fv glad_glUniformMatrix3fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; #define glUniformMatrix4fv glad_glUniformMatrix4fv typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program); GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; #define glValidateProgram glad_glValidateProgram typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); GLAPI PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d; #define glVertexAttrib1d glad_glVertexAttrib1d typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv; #define glVertexAttrib1dv glad_glVertexAttrib1dv typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); GLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; #define glVertexAttrib1f glad_glVertexAttrib1f typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; #define glVertexAttrib1fv glad_glVertexAttrib1fv typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); GLAPI PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s; #define glVertexAttrib1s glad_glVertexAttrib1s typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv; #define glVertexAttrib1sv glad_glVertexAttrib1sv typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); GLAPI PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d; #define glVertexAttrib2d glad_glVertexAttrib2d typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv; #define glVertexAttrib2dv glad_glVertexAttrib2dv typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); GLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; #define glVertexAttrib2f glad_glVertexAttrib2f typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; #define glVertexAttrib2fv glad_glVertexAttrib2fv typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); GLAPI PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s; #define glVertexAttrib2s glad_glVertexAttrib2s typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv; #define glVertexAttrib2sv glad_glVertexAttrib2sv typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); GLAPI PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d; #define glVertexAttrib3d glad_glVertexAttrib3d typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv; #define glVertexAttrib3dv glad_glVertexAttrib3dv typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); GLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; #define glVertexAttrib3f glad_glVertexAttrib3f typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; #define glVertexAttrib3fv glad_glVertexAttrib3fv typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); GLAPI PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s; #define glVertexAttrib3s glad_glVertexAttrib3s typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv; #define glVertexAttrib3sv glad_glVertexAttrib3sv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv; #define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv; #define glVertexAttrib4Niv glad_glVertexAttrib4Niv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv; #define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); GLAPI PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub; #define glVertexAttrib4Nub glad_glVertexAttrib4Nub typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv; #define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv; #define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv; #define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv; #define glVertexAttrib4bv glad_glVertexAttrib4bv typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d; #define glVertexAttrib4d glad_glVertexAttrib4d typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv; #define glVertexAttrib4dv glad_glVertexAttrib4dv typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); GLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; #define glVertexAttrib4f glad_glVertexAttrib4f typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; #define glVertexAttrib4fv glad_glVertexAttrib4fv typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv; #define glVertexAttrib4iv glad_glVertexAttrib4iv typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); GLAPI PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s; #define glVertexAttrib4s glad_glVertexAttrib4s typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv; #define glVertexAttrib4sv glad_glVertexAttrib4sv typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv; #define glVertexAttrib4ubv glad_glVertexAttrib4ubv typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv; #define glVertexAttrib4uiv glad_glVertexAttrib4uiv typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv; #define glVertexAttrib4usv glad_glVertexAttrib4usv typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; #define glVertexAttribPointer glad_glVertexAttribPointer #endif #ifndef GL_VERSION_2_1 #define GL_VERSION_2_1 1 GLAPI int GLAD_GL_VERSION_2_1; typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv; #define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv; #define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv; #define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv; #define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv; #define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv; #define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv #endif #ifndef GL_VERSION_3_0 #define GL_VERSION_3_0 1 GLAPI int GLAD_GL_VERSION_3_0; typedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); GLAPI PFNGLCOLORMASKIPROC glad_glColorMaski; #define glColorMaski glad_glColorMaski typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean *data); GLAPI PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; #define glGetBooleani_v glad_glGetBooleani_v typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint *data); GLAPI PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v; #define glGetIntegeri_v glad_glGetIntegeri_v typedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum target, GLuint index); GLAPI PFNGLENABLEIPROC glad_glEnablei; #define glEnablei glad_glEnablei typedef void (APIENTRYP PFNGLDISABLEIPROC)(GLenum target, GLuint index); GLAPI PFNGLDISABLEIPROC glad_glDisablei; #define glDisablei glad_glDisablei typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC)(GLenum target, GLuint index); GLAPI PFNGLISENABLEDIPROC glad_glIsEnabledi; #define glIsEnabledi glad_glIsEnabledi typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback; #define glBeginTransformFeedback glad_glBeginTransformFeedback typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(void); GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback; #define glEndTransformFeedback glad_glEndTransformFeedback typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); GLAPI PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange; #define glBindBufferRange glad_glBindBufferRange typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); GLAPI PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase; #define glBindBufferBase glad_glBindBufferBase typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); GLAPI PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; #define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); GLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying; #define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying typedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); GLAPI PFNGLCLAMPCOLORPROC glad_glClampColor; #define glClampColor glad_glClampColor typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender; #define glBeginConditionalRender glad_glBeginConditionalRender typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(void); GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender; #define glEndConditionalRender glad_glEndConditionalRender typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer; #define glVertexAttribIPointer glad_glVertexAttribIPointer typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint *params); GLAPI PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv; #define glGetVertexAttribIiv glad_glGetVertexAttribIiv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint *params); GLAPI PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv; #define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); GLAPI PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i; #define glVertexAttribI1i glad_glVertexAttribI1i typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); GLAPI PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i; #define glVertexAttribI2i glad_glVertexAttribI2i typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); GLAPI PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i; #define glVertexAttribI3i glad_glVertexAttribI3i typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); GLAPI PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i; #define glVertexAttribI4i glad_glVertexAttribI4i typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); GLAPI PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui; #define glVertexAttribI1ui glad_glVertexAttribI1ui typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); GLAPI PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui; #define glVertexAttribI2ui glad_glVertexAttribI2ui typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); GLAPI PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui; #define glVertexAttribI3ui glad_glVertexAttribI3ui typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); GLAPI PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui; #define glVertexAttribI4ui glad_glVertexAttribI4ui typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv; #define glVertexAttribI1iv glad_glVertexAttribI1iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv; #define glVertexAttribI2iv glad_glVertexAttribI2iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv; #define glVertexAttribI3iv glad_glVertexAttribI3iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; #define glVertexAttribI4iv glad_glVertexAttribI4iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv; #define glVertexAttribI1uiv glad_glVertexAttribI1uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv; #define glVertexAttribI2uiv glad_glVertexAttribI2uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv; #define glVertexAttribI3uiv glad_glVertexAttribI3uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; #define glVertexAttribI4uiv glad_glVertexAttribI4uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv; #define glVertexAttribI4bv glad_glVertexAttribI4bv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv; #define glVertexAttribI4sv glad_glVertexAttribI4sv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv; #define glVertexAttribI4ubv glad_glVertexAttribI4ubv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv; #define glVertexAttribI4usv glad_glVertexAttribI4usv typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params); GLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; #define glGetUniformuiv glad_glGetUniformuiv typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar *name); GLAPI PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation; #define glBindFragDataLocation glad_glBindFragDataLocation typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation; #define glGetFragDataLocation glad_glGetFragDataLocation typedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); GLAPI PFNGLUNIFORM1UIPROC glad_glUniform1ui; #define glUniform1ui glad_glUniform1ui typedef void (APIENTRYP PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); GLAPI PFNGLUNIFORM2UIPROC glad_glUniform2ui; #define glUniform2ui glad_glUniform2ui typedef void (APIENTRYP PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); GLAPI PFNGLUNIFORM3UIPROC glad_glUniform3ui; #define glUniform3ui glad_glUniform3ui typedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); GLAPI PFNGLUNIFORM4UIPROC glad_glUniform4ui; #define glUniform4ui glad_glUniform4ui typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; #define glUniform1uiv glad_glUniform1uiv typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM2UIVPROC glad_glUniform2uiv; #define glUniform2uiv glad_glUniform2uiv typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM3UIVPROC glad_glUniform3uiv; #define glUniform3uiv glad_glUniform3uiv typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM4UIVPROC glad_glUniform4uiv; #define glUniform4uiv glad_glUniform4uiv typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint *params); GLAPI PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv; #define glTexParameterIiv glad_glTexParameterIiv typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint *params); GLAPI PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv; #define glTexParameterIuiv glad_glTexParameterIuiv typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv; #define glGetTexParameterIiv glad_glGetTexParameterIiv typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint *params); GLAPI PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv; #define glGetTexParameterIuiv glad_glGetTexParameterIuiv typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint *value); GLAPI PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv; #define glClearBufferiv glad_glClearBufferiv typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint *value); GLAPI PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; #define glClearBufferuiv glad_glClearBufferuiv typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat *value); GLAPI PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv; #define glClearBufferfv glad_glClearBufferfv typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); GLAPI PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi; #define glClearBufferfi glad_glClearBufferfi typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); GLAPI PFNGLGETSTRINGIPROC glad_glGetStringi; #define glGetStringi glad_glGetStringi typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; #define glIsRenderbuffer glad_glIsRenderbuffer typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; #define glBindRenderbuffer glad_glBindRenderbuffer typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers); GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; #define glDeleteRenderbuffers glad_glDeleteRenderbuffers typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers); GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; #define glGenRenderbuffers glad_glGenRenderbuffers typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; #define glRenderbufferStorage glad_glRenderbufferStorage typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; #define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; #define glIsFramebuffer glad_glIsFramebuffer typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; #define glBindFramebuffer glad_glBindFramebuffer typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers); GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; #define glDeleteFramebuffers glad_glDeleteFramebuffers typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers); GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; #define glGenFramebuffers glad_glGenFramebuffers typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); GLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; #define glCheckFramebufferStatus glad_glCheckFramebufferStatus typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLAPI PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D; #define glFramebufferTexture1D glad_glFramebufferTexture1D typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; #define glFramebufferTexture2D glad_glFramebufferTexture2D typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); GLAPI PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D; #define glFramebufferTexture3D glad_glFramebufferTexture3D typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; #define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params); GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; #define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target); GLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; #define glGenerateMipmap glad_glGenerateMipmap typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); GLAPI PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer; #define glBlitFramebuffer glad_glBlitFramebuffer typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); GLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample; #define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); GLAPI PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer; #define glFramebufferTextureLayer glad_glFramebufferTextureLayer typedef void * (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); GLAPI PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange; #define glMapBufferRange glad_glMapBufferRange typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); GLAPI PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange; #define glFlushMappedBufferRange glad_glFlushMappedBufferRange typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array); GLAPI PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray; #define glBindVertexArray glad_glBindVertexArray typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint *arrays); GLAPI PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays; #define glDeleteVertexArrays glad_glDeleteVertexArrays typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays); GLAPI PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays; #define glGenVertexArrays glad_glGenVertexArrays typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint array); GLAPI PFNGLISVERTEXARRAYPROC glad_glIsVertexArray; #define glIsVertexArray glad_glIsVertexArray #endif #ifndef GL_VERSION_3_1 #define GL_VERSION_3_1 1 GLAPI int GLAD_GL_VERSION_3_1; typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); GLAPI PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced; #define glDrawArraysInstanced glad_glDrawArraysInstanced typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); GLAPI PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced; #define glDrawElementsInstanced glad_glDrawElementsInstanced typedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); GLAPI PFNGLTEXBUFFERPROC glad_glTexBuffer; #define glTexBuffer glad_glTexBuffer typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); GLAPI PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex; #define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); GLAPI PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData; #define glCopyBufferSubData glad_glCopyBufferSubData typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); GLAPI PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices; #define glGetUniformIndices glad_glGetUniformIndices typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); GLAPI PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv; #define glGetActiveUniformsiv glad_glGetActiveUniformsiv typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); GLAPI PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName; #define glGetActiveUniformName glad_glGetActiveUniformName typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar *uniformBlockName); GLAPI PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex; #define glGetUniformBlockIndex glad_glGetUniformBlockIndex typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); GLAPI PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv; #define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); GLAPI PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName; #define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); GLAPI PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding; #define glUniformBlockBinding glad_glUniformBlockBinding #endif #ifndef GL_VERSION_3_2 #define GL_VERSION_3_2 1 GLAPI int GLAD_GL_VERSION_3_2; typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); GLAPI PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex; #define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); GLAPI PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; #define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); GLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex; #define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); GLAPI PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex; #define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC)(GLenum mode); GLAPI PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex; #define glProvokingVertex glad_glProvokingVertex typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); GLAPI PFNGLFENCESYNCPROC glad_glFenceSync; #define glFenceSync glad_glFenceSync typedef GLboolean (APIENTRYP PFNGLISSYNCPROC)(GLsync sync); GLAPI PFNGLISSYNCPROC glad_glIsSync; #define glIsSync glad_glIsSync typedef void (APIENTRYP PFNGLDELETESYNCPROC)(GLsync sync); GLAPI PFNGLDELETESYNCPROC glad_glDeleteSync; #define glDeleteSync glad_glDeleteSync typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); GLAPI PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync; #define glClientWaitSync glad_glClientWaitSync typedef void (APIENTRYP PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); GLAPI PFNGLWAITSYNCPROC glad_glWaitSync; #define glWaitSync glad_glWaitSync typedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 *data); GLAPI PFNGLGETINTEGER64VPROC glad_glGetInteger64v; #define glGetInteger64v glad_glGetInteger64v typedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); GLAPI PFNGLGETSYNCIVPROC glad_glGetSynciv; #define glGetSynciv glad_glGetSynciv typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 *data); GLAPI PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v; #define glGetInteger64i_v glad_glGetInteger64i_v typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 *params); GLAPI PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v; #define glGetBufferParameteri64v glad_glGetBufferParameteri64v typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); GLAPI PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture; #define glFramebufferTexture glad_glFramebufferTexture typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); GLAPI PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample; #define glTexImage2DMultisample glad_glTexImage2DMultisample typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); GLAPI PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample; #define glTexImage3DMultisample glad_glTexImage3DMultisample typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat *val); GLAPI PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv; #define glGetMultisamplefv glad_glGetMultisamplefv typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); GLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski; #define glSampleMaski glad_glSampleMaski #endif #ifndef GL_VERSION_3_3 #define GL_VERSION_3_3 1 GLAPI int GLAD_GL_VERSION_3_3; typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); GLAPI PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed; #define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex; #define glGetFragDataIndex glad_glGetFragDataIndex typedef void (APIENTRYP PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint *samplers); GLAPI PFNGLGENSAMPLERSPROC glad_glGenSamplers; #define glGenSamplers glad_glGenSamplers typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint *samplers); GLAPI PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers; #define glDeleteSamplers glad_glDeleteSamplers typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC)(GLuint sampler); GLAPI PFNGLISSAMPLERPROC glad_glIsSampler; #define glIsSampler glad_glIsSampler typedef void (APIENTRYP PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); GLAPI PFNGLBINDSAMPLERPROC glad_glBindSampler; #define glBindSampler glad_glBindSampler typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); GLAPI PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri; #define glSamplerParameteri glad_glSamplerParameteri typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint *param); GLAPI PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv; #define glSamplerParameteriv glad_glSamplerParameteriv typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); GLAPI PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf; #define glSamplerParameterf glad_glSamplerParameterf typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat *param); GLAPI PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv; #define glSamplerParameterfv glad_glSamplerParameterfv typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint *param); GLAPI PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv; #define glSamplerParameterIiv glad_glSamplerParameterIiv typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint *param); GLAPI PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv; #define glSamplerParameterIuiv glad_glSamplerParameterIuiv typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint *params); GLAPI PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv; #define glGetSamplerParameteriv glad_glGetSamplerParameteriv typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint *params); GLAPI PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv; #define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat *params); GLAPI PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv; #define glGetSamplerParameterfv glad_glGetSamplerParameterfv typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint *params); GLAPI PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv; #define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target); GLAPI PFNGLQUERYCOUNTERPROC glad_glQueryCounter; #define glQueryCounter glad_glQueryCounter typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 *params); GLAPI PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v; #define glGetQueryObjecti64v glad_glGetQueryObjecti64v typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 *params); GLAPI PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v; #define glGetQueryObjectui64v glad_glGetQueryObjectui64v typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); GLAPI PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor; #define glVertexAttribDivisor glad_glVertexAttribDivisor typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); GLAPI PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui; #define glVertexAttribP1ui glad_glVertexAttribP1ui typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value); GLAPI PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv; #define glVertexAttribP1uiv glad_glVertexAttribP1uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); GLAPI PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui; #define glVertexAttribP2ui glad_glVertexAttribP2ui typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value); GLAPI PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv; #define glVertexAttribP2uiv glad_glVertexAttribP2uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); GLAPI PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui; #define glVertexAttribP3ui glad_glVertexAttribP3ui typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value); GLAPI PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv; #define glVertexAttribP3uiv glad_glVertexAttribP3uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); GLAPI PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui; #define glVertexAttribP4ui glad_glVertexAttribP4ui typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value); GLAPI PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv; #define glVertexAttribP4uiv glad_glVertexAttribP4uiv typedef void (APIENTRYP PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value); GLAPI PFNGLVERTEXP2UIPROC glad_glVertexP2ui; #define glVertexP2ui glad_glVertexP2ui typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint *value); GLAPI PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv; #define glVertexP2uiv glad_glVertexP2uiv typedef void (APIENTRYP PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value); GLAPI PFNGLVERTEXP3UIPROC glad_glVertexP3ui; #define glVertexP3ui glad_glVertexP3ui typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint *value); GLAPI PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv; #define glVertexP3uiv glad_glVertexP3uiv typedef void (APIENTRYP PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value); GLAPI PFNGLVERTEXP4UIPROC glad_glVertexP4ui; #define glVertexP4ui glad_glVertexP4ui typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint *value); GLAPI PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv; #define glVertexP4uiv glad_glVertexP4uiv typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords); GLAPI PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui; #define glTexCoordP1ui glad_glTexCoordP1ui typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint *coords); GLAPI PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv; #define glTexCoordP1uiv glad_glTexCoordP1uiv typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords); GLAPI PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui; #define glTexCoordP2ui glad_glTexCoordP2ui typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint *coords); GLAPI PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv; #define glTexCoordP2uiv glad_glTexCoordP2uiv typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords); GLAPI PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui; #define glTexCoordP3ui glad_glTexCoordP3ui typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint *coords); GLAPI PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv; #define glTexCoordP3uiv glad_glTexCoordP3uiv typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords); GLAPI PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui; #define glTexCoordP4ui glad_glTexCoordP4ui typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint *coords); GLAPI PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv; #define glTexCoordP4uiv glad_glTexCoordP4uiv typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords); GLAPI PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui; #define glMultiTexCoordP1ui glad_glMultiTexCoordP1ui typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint *coords); GLAPI PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv; #define glMultiTexCoordP1uiv glad_glMultiTexCoordP1uiv typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords); GLAPI PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui; #define glMultiTexCoordP2ui glad_glMultiTexCoordP2ui typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint *coords); GLAPI PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv; #define glMultiTexCoordP2uiv glad_glMultiTexCoordP2uiv typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords); GLAPI PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui; #define glMultiTexCoordP3ui glad_glMultiTexCoordP3ui typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint *coords); GLAPI PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv; #define glMultiTexCoordP3uiv glad_glMultiTexCoordP3uiv typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords); GLAPI PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui; #define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint *coords); GLAPI PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv; #define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv typedef void (APIENTRYP PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords); GLAPI PFNGLNORMALP3UIPROC glad_glNormalP3ui; #define glNormalP3ui glad_glNormalP3ui typedef void (APIENTRYP PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint *coords); GLAPI PFNGLNORMALP3UIVPROC glad_glNormalP3uiv; #define glNormalP3uiv glad_glNormalP3uiv typedef void (APIENTRYP PFNGLCOLORP3UIPROC)(GLenum type, GLuint color); GLAPI PFNGLCOLORP3UIPROC glad_glColorP3ui; #define glColorP3ui glad_glColorP3ui typedef void (APIENTRYP PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint *color); GLAPI PFNGLCOLORP3UIVPROC glad_glColorP3uiv; #define glColorP3uiv glad_glColorP3uiv typedef void (APIENTRYP PFNGLCOLORP4UIPROC)(GLenum type, GLuint color); GLAPI PFNGLCOLORP4UIPROC glad_glColorP4ui; #define glColorP4ui glad_glColorP4ui typedef void (APIENTRYP PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint *color); GLAPI PFNGLCOLORP4UIVPROC glad_glColorP4uiv; #define glColorP4uiv glad_glColorP4uiv typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color); GLAPI PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui; #define glSecondaryColorP3ui glad_glSecondaryColorP3ui typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint *color); GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv; #define glSecondaryColorP3uiv glad_glSecondaryColorP3uiv #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/glad/include/glad/glad_wgl.h ================================================ /* WGL loader generated by glad 0.1.34 on Wed Nov 3 06:45:32 2021. Language/Generator: C/C++ Specification: wgl APIs: wgl=1.0 Profile: - Extensions: WGL_ARB_extensions_string, WGL_EXT_extensions_string, WGL_EXT_swap_control Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: --api="wgl=1.0" --generator="c" --spec="wgl" --extensions="WGL_ARB_extensions_string,WGL_EXT_extensions_string,WGL_EXT_swap_control" Online: https://glad.dav1d.de/#language=c&specification=wgl&loader=on&api=wgl%3D1.0&extensions=WGL_ARB_extensions_string&extensions=WGL_EXT_extensions_string&extensions=WGL_EXT_swap_control */ #ifndef WINAPI #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif #ifndef NOMINMAX #define NOMINMAX 1 #endif #include #endif #include #ifndef __glad_wglext_h_ #ifdef __wglext_h_ #error WGL header already included, remove this include, glad already provides it #endif #define __glad_wglext_h_ #define __wglext_h_ #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif #ifdef __cplusplus extern "C" { #endif typedef void* (* GLADloadproc)(const char *name); #ifndef GLAPI # if defined(GLAD_GLAPI_EXPORT) # if defined(_WIN32) || defined(__CYGWIN__) # if defined(GLAD_GLAPI_EXPORT_BUILD) # if defined(__GNUC__) # define GLAPI __attribute__ ((dllexport)) extern # else # define GLAPI __declspec(dllexport) extern # endif # else # if defined(__GNUC__) # define GLAPI __attribute__ ((dllimport)) extern # else # define GLAPI __declspec(dllimport) extern # endif # endif # elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD) # define GLAPI __attribute__ ((visibility ("default"))) extern # else # define GLAPI extern # endif # else # define GLAPI extern # endif #endif GLAPI int gladLoadWGL(HDC hdc); GLAPI void gladUnloadWGL(void); GLAPI int gladLoadWGLLoader(GLADloadproc, HDC hdc); struct _GPU_DEVICE { DWORD cb; CHAR DeviceName[32]; CHAR DeviceString[128]; DWORD Flags; RECT rcVirtualScreen; }; DECLARE_HANDLE(HPBUFFERARB); DECLARE_HANDLE(HPBUFFEREXT); DECLARE_HANDLE(HVIDEOOUTPUTDEVICENV); DECLARE_HANDLE(HPVIDEODEV); DECLARE_HANDLE(HPGPUNV); DECLARE_HANDLE(HGPUNV); DECLARE_HANDLE(HVIDEOINPUTDEVICENV); typedef struct _GPU_DEVICE GPU_DEVICE; typedef struct _GPU_DEVICE *PGPU_DEVICE; #ifndef WGL_ARB_extensions_string #define WGL_ARB_extensions_string 1 GLAPI int GLAD_WGL_ARB_extensions_string; typedef const char * (APIENTRYP PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc); GLAPI PFNWGLGETEXTENSIONSSTRINGARBPROC glad_wglGetExtensionsStringARB; #define wglGetExtensionsStringARB glad_wglGetExtensionsStringARB #endif #ifndef WGL_EXT_extensions_string #define WGL_EXT_extensions_string 1 GLAPI int GLAD_WGL_EXT_extensions_string; typedef const char * (APIENTRYP PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); GLAPI PFNWGLGETEXTENSIONSSTRINGEXTPROC glad_wglGetExtensionsStringEXT; #define wglGetExtensionsStringEXT glad_wglGetExtensionsStringEXT #endif #ifndef WGL_EXT_swap_control #define WGL_EXT_swap_control 1 GLAPI int GLAD_WGL_EXT_swap_control; typedef BOOL (APIENTRYP PFNWGLSWAPINTERVALEXTPROC)(int interval); GLAPI PFNWGLSWAPINTERVALEXTPROC glad_wglSwapIntervalEXT; #define wglSwapIntervalEXT glad_wglSwapIntervalEXT typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXTPROC)(void); GLAPI PFNWGLGETSWAPINTERVALEXTPROC glad_wglGetSwapIntervalEXT; #define wglGetSwapIntervalEXT glad_wglGetSwapIntervalEXT #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/glad/src/glad.c ================================================ /* OpenGL loader generated by glad 0.1.34 on Sat Oct 30 06:22:17 2021. Language/Generator: C/C++ Specification: gl APIs: gl=3.3 Profile: core Extensions: Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: --profile="core" --api="gl=3.3" --generator="c" --spec="gl" --extensions="" Online: https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D3.3 */ #include #include #include #include static void* get_proc(const char *namez); #if defined(_WIN32) || defined(__CYGWIN__) #ifndef _WINDOWS_ #undef APIENTRY #endif #include static HMODULE libGL; typedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*); static PFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr; #ifdef _MSC_VER #ifdef __has_include #if __has_include() #define HAVE_WINAPIFAMILY 1 #endif #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ #define HAVE_WINAPIFAMILY 1 #endif #endif #ifdef HAVE_WINAPIFAMILY #include #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define IS_UWP 1 #endif #endif static int open_gl(void) { #ifndef IS_UWP libGL = LoadLibraryW(L"opengl32.dll"); if(libGL != NULL) { void (* tmp)(void); tmp = (void(*)(void)) GetProcAddress(libGL, "wglGetProcAddress"); gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE) tmp; return gladGetProcAddressPtr != NULL; } #endif return 0; } static void close_gl(void) { if(libGL != NULL) { FreeLibrary((HMODULE) libGL); libGL = NULL; } } #else #include static void* libGL; #if !defined(__APPLE__) && !defined(__HAIKU__) typedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*); static PFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr; #endif static int open_gl(void) { #ifdef __APPLE__ static const char *NAMES[] = { "../Frameworks/OpenGL.framework/OpenGL", "/Library/Frameworks/OpenGL.framework/OpenGL", "/System/Library/Frameworks/OpenGL.framework/OpenGL", "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" }; #else static const char *NAMES[] = {"libGL.so.1", "libGL.so"}; #endif unsigned int index = 0; for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) { libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL); if(libGL != NULL) { #if defined(__APPLE__) || defined(__HAIKU__) return 1; #else gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL, "glXGetProcAddressARB"); return gladGetProcAddressPtr != NULL; #endif } } return 0; } static void close_gl(void) { if(libGL != NULL) { dlclose(libGL); libGL = NULL; } } #endif static void* get_proc(const char *namez) { void* result = NULL; if(libGL == NULL) return NULL; #if !defined(__APPLE__) && !defined(__HAIKU__) if(gladGetProcAddressPtr != NULL) { result = gladGetProcAddressPtr(namez); } #endif if(result == NULL) { #if defined(_WIN32) || defined(__CYGWIN__) result = (void*)GetProcAddress((HMODULE) libGL, namez); #else result = dlsym(libGL, namez); #endif } return result; } int gladLoadGL(void) { int status = 0; if(open_gl()) { status = gladLoadGLLoader(&get_proc); close_gl(); } return status; } struct gladGLversionStruct GLVersion = { 0, 0 }; #if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) #define _GLAD_IS_SOME_NEW_VERSION 1 #endif static int max_loaded_major; static int max_loaded_minor; static const char *exts = NULL; static int num_exts_i = 0; static char **exts_i = NULL; static int get_exts(void) { #ifdef _GLAD_IS_SOME_NEW_VERSION if(max_loaded_major < 3) { #endif exts = (const char *)glGetString(GL_EXTENSIONS); #ifdef _GLAD_IS_SOME_NEW_VERSION } else { unsigned int index; num_exts_i = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i); if (num_exts_i > 0) { exts_i = (char **)malloc((size_t)num_exts_i * (sizeof *exts_i)); } if (exts_i == NULL) { return 0; } for(index = 0; index < (unsigned)num_exts_i; index++) { const char *gl_str_tmp = (const char*)glGetStringi(GL_EXTENSIONS, index); size_t len = strlen(gl_str_tmp); char *local_str = (char*)malloc((len+1) * sizeof(char)); if(local_str != NULL) { memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char)); } exts_i[index] = local_str; } } #endif return 1; } static void free_exts(void) { if (exts_i != NULL) { int index; for(index = 0; index < num_exts_i; index++) { free((char *)exts_i[index]); } free((void *)exts_i); exts_i = NULL; } } static int has_ext(const char *ext) { #ifdef _GLAD_IS_SOME_NEW_VERSION if(max_loaded_major < 3) { #endif const char *extensions; const char *loc; const char *terminator; extensions = exts; if(extensions == NULL || ext == NULL) { return 0; } while(1) { loc = strstr(extensions, ext); if(loc == NULL) { return 0; } terminator = loc + strlen(ext); if((loc == extensions || *(loc - 1) == ' ') && (*terminator == ' ' || *terminator == '\0')) { return 1; } extensions = terminator; } #ifdef _GLAD_IS_SOME_NEW_VERSION } else { int index; if(exts_i == NULL) return 0; for(index = 0; index < num_exts_i; index++) { const char *e = exts_i[index]; if(exts_i[index] != NULL && strcmp(e, ext) == 0) { return 1; } } } #endif return 0; } int GLAD_GL_VERSION_1_0 = 0; int GLAD_GL_VERSION_1_1 = 0; int GLAD_GL_VERSION_1_2 = 0; int GLAD_GL_VERSION_1_3 = 0; int GLAD_GL_VERSION_1_4 = 0; int GLAD_GL_VERSION_1_5 = 0; int GLAD_GL_VERSION_2_0 = 0; int GLAD_GL_VERSION_2_1 = 0; int GLAD_GL_VERSION_3_0 = 0; int GLAD_GL_VERSION_3_1 = 0; int GLAD_GL_VERSION_3_2 = 0; int GLAD_GL_VERSION_3_3 = 0; PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL; PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL; PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL; PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL; PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; PFNGLBUFFERDATAPROC glad_glBufferData = NULL; PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; PFNGLCLAMPCOLORPROC glad_glClampColor = NULL; PFNGLCLEARPROC glad_glClear = NULL; PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; PFNGLCLEARCOLORPROC glad_glClearColor = NULL; PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL; PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL; PFNGLCOLORMASKPROC glad_glColorMask = NULL; PFNGLCOLORMASKIPROC glad_glColorMaski = NULL; PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL; PFNGLCOLORP3UIVPROC glad_glColorP3uiv = NULL; PFNGLCOLORP4UIPROC glad_glColorP4ui = NULL; PFNGLCOLORP4UIVPROC glad_glColorP4uiv = NULL; PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL; PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL; PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL; PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; PFNGLCREATESHADERPROC glad_glCreateShader = NULL; PFNGLCULLFACEPROC glad_glCullFace = NULL; PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL; PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; PFNGLDELETESYNCPROC glad_glDeleteSync = NULL; PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL; PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; PFNGLDISABLEPROC glad_glDisable = NULL; PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; PFNGLDISABLEIPROC glad_glDisablei = NULL; PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL; PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; PFNGLENABLEPROC glad_glEnable = NULL; PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; PFNGLENABLEIPROC glad_glEnablei = NULL; PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL; PFNGLENDQUERYPROC glad_glEndQuery = NULL; PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; PFNGLFENCESYNCPROC glad_glFenceSync = NULL; PFNGLFINISHPROC glad_glFinish = NULL; PFNGLFLUSHPROC glad_glFlush = NULL; PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL; PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL; PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL; PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; PFNGLFRONTFACEPROC glad_glFrontFace = NULL; PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; PFNGLGENQUERIESPROC glad_glGenQueries = NULL; PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL; PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL; PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL; PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL; PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL; PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL; PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL; PFNGLGETERRORPROC glad_glGetError = NULL; PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL; PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL; PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL; PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL; PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL; PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL; PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL; PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL; PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL; PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL; PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL; PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; PFNGLGETSTRINGPROC glad_glGetString = NULL; PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL; PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL; PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL; PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL; PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL; PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL; PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL; PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; PFNGLHINTPROC glad_glHint = NULL; PFNGLISBUFFERPROC glad_glIsBuffer = NULL; PFNGLISENABLEDPROC glad_glIsEnabled = NULL; PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL; PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; PFNGLISPROGRAMPROC glad_glIsProgram = NULL; PFNGLISQUERYPROC glad_glIsQuery = NULL; PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; PFNGLISSAMPLERPROC glad_glIsSampler = NULL; PFNGLISSHADERPROC glad_glIsShader = NULL; PFNGLISSYNCPROC glad_glIsSync = NULL; PFNGLISTEXTUREPROC glad_glIsTexture = NULL; PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; PFNGLLOGICOPPROC glad_glLogicOp = NULL; PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL; PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL; PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL; PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL; PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL; PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL; PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL; PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv = NULL; PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL; PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL; PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL; PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL; PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL; PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL; PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL; PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL; PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL; PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL; PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL; PFNGLPOINTSIZEPROC glad_glPointSize = NULL; PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL; PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL; PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL; PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL; PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; PFNGLREADPIXELSPROC glad_glReadPixels = NULL; PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL; PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL; PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL; PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL; PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL; PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL; PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL; PFNGLSCISSORPROC glad_glScissor = NULL; PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui = NULL; PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL; PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; PFNGLSTENCILOPPROC glad_glStencilOp = NULL; PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL; PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui = NULL; PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv = NULL; PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui = NULL; PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv = NULL; PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui = NULL; PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv = NULL; PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui = NULL; PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv = NULL; PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL; PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL; PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL; PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL; PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL; PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL; PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL; PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL; PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL; PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL; PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL; PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL; PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL; PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL; PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL; PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL; PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL; PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL; PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL; PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL; PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL; PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL; PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL; PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL; PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL; PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL; PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL; PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL; PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL; PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL; PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL; PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL; PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL; PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL; PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL; PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL; PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL; PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL; PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL; PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL; PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL; PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL; PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL; PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL; PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL; PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL; PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL; PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL; PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL; PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL; PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL; PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL; PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL; PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL; PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL; PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL; PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL; PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL; PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL; PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; PFNGLVERTEXP2UIPROC glad_glVertexP2ui = NULL; PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv = NULL; PFNGLVERTEXP3UIPROC glad_glVertexP3ui = NULL; PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv = NULL; PFNGLVERTEXP4UIPROC glad_glVertexP4ui = NULL; PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv = NULL; PFNGLVIEWPORTPROC glad_glViewport = NULL; PFNGLWAITSYNCPROC glad_glWaitSync = NULL; static void load_GL_VERSION_1_0(GLADloadproc load) { if(!GLAD_GL_VERSION_1_0) return; glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace"); glad_glHint = (PFNGLHINTPROC)load("glHint"); glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth"); glad_glPointSize = (PFNGLPOINTSIZEPROC)load("glPointSize"); glad_glPolygonMode = (PFNGLPOLYGONMODEPROC)load("glPolygonMode"); glad_glScissor = (PFNGLSCISSORPROC)load("glScissor"); glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf"); glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv"); glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri"); glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv"); glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC)load("glTexImage1D"); glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D"); glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC)load("glDrawBuffer"); glad_glClear = (PFNGLCLEARPROC)load("glClear"); glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor"); glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil"); glad_glClearDepth = (PFNGLCLEARDEPTHPROC)load("glClearDepth"); glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask"); glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask"); glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask"); glad_glDisable = (PFNGLDISABLEPROC)load("glDisable"); glad_glEnable = (PFNGLENABLEPROC)load("glEnable"); glad_glFinish = (PFNGLFINISHPROC)load("glFinish"); glad_glFlush = (PFNGLFLUSHPROC)load("glFlush"); glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc"); glad_glLogicOp = (PFNGLLOGICOPPROC)load("glLogicOp"); glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc"); glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp"); glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc"); glad_glPixelStoref = (PFNGLPIXELSTOREFPROC)load("glPixelStoref"); glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei"); glad_glReadBuffer = (PFNGLREADBUFFERPROC)load("glReadBuffer"); glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels"); glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv"); glad_glGetDoublev = (PFNGLGETDOUBLEVPROC)load("glGetDoublev"); glad_glGetError = (PFNGLGETERRORPROC)load("glGetError"); glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv"); glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv"); glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString"); glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC)load("glGetTexImage"); glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv"); glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv"); glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)load("glGetTexLevelParameterfv"); glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)load("glGetTexLevelParameteriv"); glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled"); glad_glDepthRange = (PFNGLDEPTHRANGEPROC)load("glDepthRange"); glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport"); } static void load_GL_VERSION_1_1(GLADloadproc load) { if(!GLAD_GL_VERSION_1_1) return; glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays"); glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements"); glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset"); glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC)load("glCopyTexImage1D"); glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D"); glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC)load("glCopyTexSubImage1D"); glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D"); glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC)load("glTexSubImage1D"); glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D"); glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture"); glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures"); glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures"); glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture"); } static void load_GL_VERSION_1_2(GLADloadproc load) { if(!GLAD_GL_VERSION_1_2) return; glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load("glDrawRangeElements"); glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load("glTexImage3D"); glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load("glTexSubImage3D"); glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load("glCopyTexSubImage3D"); } static void load_GL_VERSION_1_3(GLADloadproc load) { if(!GLAD_GL_VERSION_1_3) return; glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture"); glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage"); glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load("glCompressedTexImage3D"); glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D"); glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)load("glCompressedTexImage1D"); glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load("glCompressedTexSubImage3D"); glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D"); glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)load("glCompressedTexSubImage1D"); glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)load("glGetCompressedTexImage"); } static void load_GL_VERSION_1_4(GLADloadproc load) { if(!GLAD_GL_VERSION_1_4) return; glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate"); glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)load("glMultiDrawArrays"); glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)load("glMultiDrawElements"); glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC)load("glPointParameterf"); glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)load("glPointParameterfv"); glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC)load("glPointParameteri"); glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)load("glPointParameteriv"); glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor"); glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation"); } static void load_GL_VERSION_1_5(GLADloadproc load) { if(!GLAD_GL_VERSION_1_5) return; glad_glGenQueries = (PFNGLGENQUERIESPROC)load("glGenQueries"); glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load("glDeleteQueries"); glad_glIsQuery = (PFNGLISQUERYPROC)load("glIsQuery"); glad_glBeginQuery = (PFNGLBEGINQUERYPROC)load("glBeginQuery"); glad_glEndQuery = (PFNGLENDQUERYPROC)load("glEndQuery"); glad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load("glGetQueryiv"); glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)load("glGetQueryObjectiv"); glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load("glGetQueryObjectuiv"); glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer"); glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers"); glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers"); glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer"); glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData"); glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData"); glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)load("glGetBufferSubData"); glad_glMapBuffer = (PFNGLMAPBUFFERPROC)load("glMapBuffer"); glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load("glUnmapBuffer"); glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv"); glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv"); } static void load_GL_VERSION_2_0(GLADloadproc load) { if(!GLAD_GL_VERSION_2_0) return; glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate"); glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load("glDrawBuffers"); glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate"); glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate"); glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate"); glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader"); glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation"); glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader"); glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram"); glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader"); glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram"); glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader"); glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader"); glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray"); glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray"); glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib"); glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform"); glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders"); glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation"); glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv"); glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog"); glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv"); glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog"); glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource"); glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation"); glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv"); glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv"); glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)load("glGetVertexAttribdv"); glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv"); glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv"); glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv"); glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram"); glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader"); glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram"); glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource"); glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram"); glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f"); glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f"); glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f"); glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f"); glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i"); glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i"); glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i"); glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i"); glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv"); glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv"); glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv"); glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv"); glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv"); glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv"); glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv"); glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv"); glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv"); glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv"); glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv"); glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram"); glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)load("glVertexAttrib1d"); glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)load("glVertexAttrib1dv"); glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f"); glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv"); glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)load("glVertexAttrib1s"); glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)load("glVertexAttrib1sv"); glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)load("glVertexAttrib2d"); glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)load("glVertexAttrib2dv"); glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f"); glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv"); glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)load("glVertexAttrib2s"); glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)load("glVertexAttrib2sv"); glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)load("glVertexAttrib3d"); glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)load("glVertexAttrib3dv"); glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f"); glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv"); glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)load("glVertexAttrib3s"); glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)load("glVertexAttrib3sv"); glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)load("glVertexAttrib4Nbv"); glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)load("glVertexAttrib4Niv"); glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)load("glVertexAttrib4Nsv"); glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)load("glVertexAttrib4Nub"); glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)load("glVertexAttrib4Nubv"); glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)load("glVertexAttrib4Nuiv"); glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)load("glVertexAttrib4Nusv"); glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)load("glVertexAttrib4bv"); glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)load("glVertexAttrib4d"); glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)load("glVertexAttrib4dv"); glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f"); glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv"); glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)load("glVertexAttrib4iv"); glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)load("glVertexAttrib4s"); glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)load("glVertexAttrib4sv"); glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)load("glVertexAttrib4ubv"); glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)load("glVertexAttrib4uiv"); glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)load("glVertexAttrib4usv"); glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer"); } static void load_GL_VERSION_2_1(GLADloadproc load) { if(!GLAD_GL_VERSION_2_1) return; glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load("glUniformMatrix2x3fv"); glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load("glUniformMatrix3x2fv"); glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load("glUniformMatrix2x4fv"); glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load("glUniformMatrix4x2fv"); glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load("glUniformMatrix3x4fv"); glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load("glUniformMatrix4x3fv"); } static void load_GL_VERSION_3_0(GLADloadproc load) { if(!GLAD_GL_VERSION_3_0) return; glad_glColorMaski = (PFNGLCOLORMASKIPROC)load("glColorMaski"); glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)load("glGetBooleani_v"); glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v"); glad_glEnablei = (PFNGLENABLEIPROC)load("glEnablei"); glad_glDisablei = (PFNGLDISABLEIPROC)load("glDisablei"); glad_glIsEnabledi = (PFNGLISENABLEDIPROC)load("glIsEnabledi"); glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load("glBeginTransformFeedback"); glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load("glEndTransformFeedback"); glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange"); glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase"); glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load("glTransformFeedbackVaryings"); glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load("glGetTransformFeedbackVarying"); glad_glClampColor = (PFNGLCLAMPCOLORPROC)load("glClampColor"); glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)load("glBeginConditionalRender"); glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)load("glEndConditionalRender"); glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load("glVertexAttribIPointer"); glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load("glGetVertexAttribIiv"); glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load("glGetVertexAttribIuiv"); glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)load("glVertexAttribI1i"); glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)load("glVertexAttribI2i"); glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)load("glVertexAttribI3i"); glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load("glVertexAttribI4i"); glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)load("glVertexAttribI1ui"); glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)load("glVertexAttribI2ui"); glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)load("glVertexAttribI3ui"); glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load("glVertexAttribI4ui"); glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)load("glVertexAttribI1iv"); glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)load("glVertexAttribI2iv"); glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)load("glVertexAttribI3iv"); glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load("glVertexAttribI4iv"); glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)load("glVertexAttribI1uiv"); glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)load("glVertexAttribI2uiv"); glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)load("glVertexAttribI3uiv"); glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load("glVertexAttribI4uiv"); glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)load("glVertexAttribI4bv"); glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)load("glVertexAttribI4sv"); glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)load("glVertexAttribI4ubv"); glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)load("glVertexAttribI4usv"); glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load("glGetUniformuiv"); glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)load("glBindFragDataLocation"); glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load("glGetFragDataLocation"); glad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load("glUniform1ui"); glad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load("glUniform2ui"); glad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load("glUniform3ui"); glad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load("glUniform4ui"); glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load("glUniform1uiv"); glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load("glUniform2uiv"); glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load("glUniform3uiv"); glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load("glUniform4uiv"); glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)load("glTexParameterIiv"); glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)load("glTexParameterIuiv"); glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)load("glGetTexParameterIiv"); glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)load("glGetTexParameterIuiv"); glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load("glClearBufferiv"); glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load("glClearBufferuiv"); glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load("glClearBufferfv"); glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load("glClearBufferfi"); glad_glGetStringi = (PFNGLGETSTRINGIPROC)load("glGetStringi"); glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer"); glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer"); glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers"); glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers"); glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage"); glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv"); glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer"); glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer"); glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers"); glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers"); glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus"); glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load("glFramebufferTexture1D"); glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D"); glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load("glFramebufferTexture3D"); glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer"); glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv"); glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap"); glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load("glBlitFramebuffer"); glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glRenderbufferStorageMultisample"); glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load("glFramebufferTextureLayer"); glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load("glMapBufferRange"); glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load("glFlushMappedBufferRange"); glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load("glBindVertexArray"); glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load("glDeleteVertexArrays"); glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load("glGenVertexArrays"); glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load("glIsVertexArray"); } static void load_GL_VERSION_3_1(GLADloadproc load) { if(!GLAD_GL_VERSION_3_1) return; glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load("glDrawArraysInstanced"); glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load("glDrawElementsInstanced"); glad_glTexBuffer = (PFNGLTEXBUFFERPROC)load("glTexBuffer"); glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)load("glPrimitiveRestartIndex"); glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load("glCopyBufferSubData"); glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load("glGetUniformIndices"); glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load("glGetActiveUniformsiv"); glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)load("glGetActiveUniformName"); glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load("glGetUniformBlockIndex"); glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv"); glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName"); glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding"); glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange"); glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase"); glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v"); } static void load_GL_VERSION_3_2(GLADloadproc load) { if(!GLAD_GL_VERSION_3_2) return; glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load("glDrawElementsBaseVertex"); glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)load("glDrawRangeElementsBaseVertex"); glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)load("glDrawElementsInstancedBaseVertex"); glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)load("glMultiDrawElementsBaseVertex"); glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)load("glProvokingVertex"); glad_glFenceSync = (PFNGLFENCESYNCPROC)load("glFenceSync"); glad_glIsSync = (PFNGLISSYNCPROC)load("glIsSync"); glad_glDeleteSync = (PFNGLDELETESYNCPROC)load("glDeleteSync"); glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load("glClientWaitSync"); glad_glWaitSync = (PFNGLWAITSYNCPROC)load("glWaitSync"); glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load("glGetInteger64v"); glad_glGetSynciv = (PFNGLGETSYNCIVPROC)load("glGetSynciv"); glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)load("glGetInteger64i_v"); glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)load("glGetBufferParameteri64v"); glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)load("glFramebufferTexture"); glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)load("glTexImage2DMultisample"); glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)load("glTexImage3DMultisample"); glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load("glGetMultisamplefv"); glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load("glSampleMaski"); } static void load_GL_VERSION_3_3(GLADloadproc load) { if(!GLAD_GL_VERSION_3_3) return; glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)load("glBindFragDataLocationIndexed"); glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)load("glGetFragDataIndex"); glad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load("glGenSamplers"); glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load("glDeleteSamplers"); glad_glIsSampler = (PFNGLISSAMPLERPROC)load("glIsSampler"); glad_glBindSampler = (PFNGLBINDSAMPLERPROC)load("glBindSampler"); glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load("glSamplerParameteri"); glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load("glSamplerParameteriv"); glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load("glSamplerParameterf"); glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load("glSamplerParameterfv"); glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)load("glSamplerParameterIiv"); glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)load("glSamplerParameterIuiv"); glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load("glGetSamplerParameteriv"); glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)load("glGetSamplerParameterIiv"); glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load("glGetSamplerParameterfv"); glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)load("glGetSamplerParameterIuiv"); glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC)load("glQueryCounter"); glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)load("glGetQueryObjecti64v"); glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)load("glGetQueryObjectui64v"); glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load("glVertexAttribDivisor"); glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)load("glVertexAttribP1ui"); glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)load("glVertexAttribP1uiv"); glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)load("glVertexAttribP2ui"); glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)load("glVertexAttribP2uiv"); glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)load("glVertexAttribP3ui"); glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)load("glVertexAttribP3uiv"); glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)load("glVertexAttribP4ui"); glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)load("glVertexAttribP4uiv"); glad_glVertexP2ui = (PFNGLVERTEXP2UIPROC)load("glVertexP2ui"); glad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)load("glVertexP2uiv"); glad_glVertexP3ui = (PFNGLVERTEXP3UIPROC)load("glVertexP3ui"); glad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)load("glVertexP3uiv"); glad_glVertexP4ui = (PFNGLVERTEXP4UIPROC)load("glVertexP4ui"); glad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)load("glVertexP4uiv"); glad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)load("glTexCoordP1ui"); glad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)load("glTexCoordP1uiv"); glad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)load("glTexCoordP2ui"); glad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)load("glTexCoordP2uiv"); glad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)load("glTexCoordP3ui"); glad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)load("glTexCoordP3uiv"); glad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)load("glTexCoordP4ui"); glad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)load("glTexCoordP4uiv"); glad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)load("glMultiTexCoordP1ui"); glad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)load("glMultiTexCoordP1uiv"); glad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)load("glMultiTexCoordP2ui"); glad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)load("glMultiTexCoordP2uiv"); glad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)load("glMultiTexCoordP3ui"); glad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)load("glMultiTexCoordP3uiv"); glad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)load("glMultiTexCoordP4ui"); glad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)load("glMultiTexCoordP4uiv"); glad_glNormalP3ui = (PFNGLNORMALP3UIPROC)load("glNormalP3ui"); glad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC)load("glNormalP3uiv"); glad_glColorP3ui = (PFNGLCOLORP3UIPROC)load("glColorP3ui"); glad_glColorP3uiv = (PFNGLCOLORP3UIVPROC)load("glColorP3uiv"); glad_glColorP4ui = (PFNGLCOLORP4UIPROC)load("glColorP4ui"); glad_glColorP4uiv = (PFNGLCOLORP4UIVPROC)load("glColorP4uiv"); glad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)load("glSecondaryColorP3ui"); glad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)load("glSecondaryColorP3uiv"); } static int find_extensionsGL(void) { if (!get_exts()) return 0; (void)&has_ext; free_exts(); return 1; } static void find_coreGL(void) { /* Thank you @elmindreda * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176 * https://github.com/glfw/glfw/blob/master/src/context.c#L36 */ int i, major, minor; const char* version; const char* prefixes[] = { "OpenGL ES-CM ", "OpenGL ES-CL ", "OpenGL ES ", NULL }; version = (const char*) glGetString(GL_VERSION); if (!version) return; for (i = 0; prefixes[i]; i++) { const size_t length = strlen(prefixes[i]); if (strncmp(version, prefixes[i], length) == 0) { version += length; break; } } /* PR #18 */ #ifdef _MSC_VER sscanf_s(version, "%d.%d", &major, &minor); #else sscanf(version, "%d.%d", &major, &minor); #endif GLVersion.major = major; GLVersion.minor = minor; max_loaded_major = major; max_loaded_minor = minor; GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3; if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 3)) { max_loaded_major = 3; max_loaded_minor = 3; } } int gladLoadGLLoader(GLADloadproc load) { GLVersion.major = 0; GLVersion.minor = 0; glGetString = (PFNGLGETSTRINGPROC)load("glGetString"); if(glGetString == NULL) return 0; if(glGetString(GL_VERSION) == NULL) return 0; find_coreGL(); load_GL_VERSION_1_0(load); load_GL_VERSION_1_1(load); load_GL_VERSION_1_2(load); load_GL_VERSION_1_3(load); load_GL_VERSION_1_4(load); load_GL_VERSION_1_5(load); load_GL_VERSION_2_0(load); load_GL_VERSION_2_1(load); load_GL_VERSION_3_0(load); load_GL_VERSION_3_1(load); load_GL_VERSION_3_2(load); load_GL_VERSION_3_3(load); if (!find_extensionsGL()) return 0; return GLVersion.major != 0 || GLVersion.minor != 0; } ================================================ FILE: deps/glad/src/glad_wgl.c ================================================ /* WGL loader generated by glad 0.1.34 on Wed Nov 3 06:45:32 2021. Language/Generator: C/C++ Specification: wgl APIs: wgl=1.0 Profile: - Extensions: WGL_ARB_extensions_string, WGL_EXT_extensions_string, WGL_EXT_swap_control Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: --api="wgl=1.0" --generator="c" --spec="wgl" --extensions="WGL_ARB_extensions_string,WGL_EXT_extensions_string,WGL_EXT_swap_control" Online: https://glad.dav1d.de/#language=c&specification=wgl&loader=on&api=wgl%3D1.0&extensions=WGL_ARB_extensions_string&extensions=WGL_EXT_extensions_string&extensions=WGL_EXT_swap_control */ #include #include #include #include static void* get_proc(const char *namez); #if defined(_WIN32) || defined(__CYGWIN__) #ifndef _WINDOWS_ #undef APIENTRY #endif #include static HMODULE libGL; typedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*); static PFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr; #ifdef _MSC_VER #ifdef __has_include #if __has_include() #define HAVE_WINAPIFAMILY 1 #endif #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ #define HAVE_WINAPIFAMILY 1 #endif #endif #ifdef HAVE_WINAPIFAMILY #include #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define IS_UWP 1 #endif #endif static int open_wgl(void) { #ifndef IS_UWP libGL = LoadLibraryW(L"opengl32.dll"); if(libGL != NULL) { void (* tmp)(void); tmp = (void(*)(void)) GetProcAddress(libGL, "wglGetProcAddress"); gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE) tmp; return gladGetProcAddressPtr != NULL; } #endif return 0; } static void close_wgl(void) { if(libGL != NULL) { FreeLibrary((HMODULE) libGL); libGL = NULL; } } #else #include static void* libGL; #if !defined(__APPLE__) && !defined(__HAIKU__) typedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*); static PFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr; #endif static int open_wgl(void) { #ifdef __APPLE__ static const char *NAMES[] = { "../Frameworks/OpenGL.framework/OpenGL", "/Library/Frameworks/OpenGL.framework/OpenGL", "/System/Library/Frameworks/OpenGL.framework/OpenGL", "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" }; #else static const char *NAMES[] = {"libGL.so.1", "libGL.so"}; #endif unsigned int index = 0; for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) { libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL); if(libGL != NULL) { #if defined(__APPLE__) || defined(__HAIKU__) return 1; #else gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL, "glXGetProcAddressARB"); return gladGetProcAddressPtr != NULL; #endif } } return 0; } static void close_wgl(void) { if(libGL != NULL) { dlclose(libGL); libGL = NULL; } } #endif static void* get_proc(const char *namez) { void* result = NULL; if(libGL == NULL) return NULL; #if !defined(__APPLE__) && !defined(__HAIKU__) if(gladGetProcAddressPtr != NULL) { result = gladGetProcAddressPtr(namez); } #endif if(result == NULL) { #if defined(_WIN32) || defined(__CYGWIN__) result = (void*)GetProcAddress((HMODULE) libGL, namez); #else result = dlsym(libGL, namez); #endif } return result; } int gladLoadWGL(HDC hdc) { int status = 0; if(open_wgl()) { status = gladLoadWGLLoader((GLADloadproc)get_proc, hdc); } return status; } void gladUnloadGLX(void) { close_wgl(); } static HDC GLADWGLhdc = (HDC)INVALID_HANDLE_VALUE; static int get_exts(void) { return 1; } static void free_exts(void) { return; } static int has_ext(const char *ext) { const char *terminator; const char *loc; const char *extensions; if(wglGetExtensionsStringEXT == NULL && wglGetExtensionsStringARB == NULL) return 0; if(wglGetExtensionsStringARB == NULL || GLADWGLhdc == INVALID_HANDLE_VALUE) extensions = wglGetExtensionsStringEXT(); else extensions = wglGetExtensionsStringARB(GLADWGLhdc); if(extensions == NULL || ext == NULL) return 0; while(1) { loc = strstr(extensions, ext); if(loc == NULL) break; terminator = loc + strlen(ext); if((loc == extensions || *(loc - 1) == ' ') && (*terminator == ' ' || *terminator == '\0')) { return 1; } extensions = terminator; } return 0; } int GLAD_WGL_VERSION_1_0 = 0; int GLAD_WGL_ARB_extensions_string = 0; int GLAD_WGL_EXT_extensions_string = 0; int GLAD_WGL_EXT_swap_control = 0; PFNWGLGETEXTENSIONSSTRINGARBPROC glad_wglGetExtensionsStringARB = NULL; PFNWGLGETEXTENSIONSSTRINGEXTPROC glad_wglGetExtensionsStringEXT = NULL; PFNWGLSWAPINTERVALEXTPROC glad_wglSwapIntervalEXT = NULL; PFNWGLGETSWAPINTERVALEXTPROC glad_wglGetSwapIntervalEXT = NULL; static void load_WGL_ARB_extensions_string(GLADloadproc load) { if(!GLAD_WGL_ARB_extensions_string) return; glad_wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)load("wglGetExtensionsStringARB"); } static void load_WGL_EXT_extensions_string(GLADloadproc load) { if(!GLAD_WGL_EXT_extensions_string) return; glad_wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)load("wglGetExtensionsStringEXT"); } static void load_WGL_EXT_swap_control(GLADloadproc load) { if(!GLAD_WGL_EXT_swap_control) return; glad_wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)load("wglSwapIntervalEXT"); glad_wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)load("wglGetSwapIntervalEXT"); } static int find_extensionsWGL(void) { if (!get_exts()) return 0; GLAD_WGL_ARB_extensions_string = has_ext("WGL_ARB_extensions_string"); GLAD_WGL_EXT_extensions_string = has_ext("WGL_EXT_extensions_string"); GLAD_WGL_EXT_swap_control = has_ext("WGL_EXT_swap_control"); free_exts(); return 1; } static void find_coreWGL(HDC hdc) { GLADWGLhdc = hdc; } int gladLoadWGLLoader(GLADloadproc load, HDC hdc) { wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)load("wglGetExtensionsStringARB"); wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)load("wglGetExtensionsStringEXT"); if(wglGetExtensionsStringARB == NULL && wglGetExtensionsStringEXT == NULL) return 0; find_coreWGL(hdc); if (!find_extensionsWGL()) return 0; load_WGL_ARB_extensions_string(load); load_WGL_EXT_extensions_string(load); load_WGL_EXT_swap_control(load); return 1; } ================================================ FILE: deps/inih/CMakeLists.txt ================================================ add_library(inih STATIC EXCLUDE_FROM_ALL ini.c ini.h) target_compile_definitions(inih PRIVATE INI_ALLOW_NO_VALUE INI_CALL_HANDLER_ON_NEW_SECTION) target_include_directories(inih PUBLIC .) set_target_properties(inih PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) ================================================ FILE: deps/inih/LICENSE.txt ================================================ The "inih" library is distributed under the New BSD license: Copyright (c) 2009, Ben Hoyt All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Ben Hoyt nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: deps/inih/README.md ================================================ # inih (INI Not Invented Here) [![Tests](https://github.com/benhoyt/inih/actions/workflows/tests.yml/badge.svg)](https://github.com/benhoyt/inih/actions/workflows/tests.yml) **inih (INI Not Invented Here)** is a simple [.INI file](http://en.wikipedia.org/wiki/INI_file) parser written in C. It's only a couple of pages of code, and it was designed to be _small and simple_, so it's good for embedded systems. It's also more or less compatible with Python's [ConfigParser](http://docs.python.org/library/configparser.html) style of .INI files, including RFC 822-style multi-line syntax and `name: value` entries. To use it, just give `ini_parse()` an INI file, and it will call a callback for every `name=value` pair parsed, giving you strings for the section, name, and value. It's done this way ("SAX style") because it works well on low-memory embedded systems, but also because it makes for a KISS implementation. You can also call `ini_parse_file()` to parse directly from a `FILE*` object, `ini_parse_string()` to parse data from a string, or `ini_parse_stream()` to parse using a custom fgets-style reader function for custom I/O. Download a release, browse the source, or read about [how to use inih in a DRY style](http://blog.brush.co.nz/2009/08/xmacros/) with X-Macros. ## Compile-time options ## You can control various aspects of inih using preprocessor defines: ### Syntax options ### * **Multi-line entries:** By default, inih supports multi-line entries in the style of Python's ConfigParser. To disable, add `-DINI_ALLOW_MULTILINE=0`. * **UTF-8 BOM:** By default, inih allows a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of INI files. To disable, add `-DINI_ALLOW_BOM=0`. * **Inline comments:** By default, inih allows inline comments with the `;` character. To disable, add `-DINI_ALLOW_INLINE_COMMENTS=0`. You can also specify which character(s) start an inline comment using `INI_INLINE_COMMENT_PREFIXES`. * **Start-of-line comments:** By default, inih allows both `;` and `#` to start a comment at the beginning of a line. You can override this by changing `INI_START_COMMENT_PREFIXES`. * **Allow no value:** By default, inih treats a name with no value (no `=` or `:` on the line) as an error. To allow names with no values, add `-DINI_ALLOW_NO_VALUE=1`, and inih will call your handler function with value set to NULL. ### Parsing options ### * **Stop on first error:** By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add `-DINI_STOP_ON_FIRST_ERROR=1`. * **Report line numbers:** By default, the `ini_handler` callback doesn't receive the line number as a parameter. If you need that, add `-DINI_HANDLER_LINENO=1`. * **Call handler on new section:** By default, inih only calls the handler on each `name=value` pair. To detect new sections (e.g., the INI file has multiple sections with the same name), add `-DINI_CALL_HANDLER_ON_NEW_SECTION=1`. Your handler function will then be called each time a new section is encountered, with `section` set to the new section name but `name` and `value` set to NULL. ### Memory options ### * **Stack vs heap:** By default, inih creates a fixed-sized line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`. * **Maximum line length:** The default maximum line length (for stack or heap) is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`. Note that `INI_MAX_LINE` must be 3 more than the longest line (due to `\r`, `\n`, and the NUL). * **Initial malloc size:** `INI_INITIAL_ALLOC` specifies the initial malloc size when using the heap. It defaults to 200 bytes. * **Allow realloc:** By default when using the heap (`-DINI_USE_STACK=0`), inih allocates a fixed-sized buffer of `INI_INITIAL_ALLOC` bytes. To allow this to grow to `INI_MAX_LINE` bytes, doubling if needed, set `-DINI_ALLOW_REALLOC=1`. * **Custom allocator:** By default when using the heap, the standard library's `malloc`, `free`, and `realloc` functions are used; to use a custom allocator, specify `-DINI_CUSTOM_ALLOCATOR=1` (and `-DINI_USE_STACK=0`). You must define and link functions named `ini_malloc`, `ini_free`, and (if `INI_ALLOW_REALLOC` is set) `ini_realloc`, which must have the same signatures as the `stdlib.h` memory allocation functions. ## Simple example in C ## ```c #include #include #include #include "../ini.h" typedef struct { int version; const char* name; const char* email; } configuration; static int handler(void* user, const char* section, const char* name, const char* value) { configuration* pconfig = (configuration*)user; #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 if (MATCH("protocol", "version")) { pconfig->version = atoi(value); } else if (MATCH("user", "name")) { pconfig->name = strdup(value); } else if (MATCH("user", "email")) { pconfig->email = strdup(value); } else { return 0; /* unknown section/name, error */ } return 1; } int main(int argc, char* argv[]) { configuration config; if (ini_parse("test.ini", handler, &config) < 0) { printf("Can't load 'test.ini'\n"); return 1; } printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n", config.version, config.name, config.email); return 0; } ``` ## C++ example ## If you're into C++ and the STL, there is also an easy-to-use [INIReader class](https://github.com/benhoyt/inih/blob/master/cpp/INIReader.h) that stores values in a `map` and lets you `Get()` them: ```cpp #include #include "INIReader.h" int main() { INIReader reader("../examples/test.ini"); if (reader.ParseError() < 0) { std::cout << "Can't load 'test.ini'\n"; return 1; } std::cout << "Config loaded from 'test.ini': version=" << reader.GetInteger("protocol", "version", -1) << ", name=" << reader.Get("user", "name", "UNKNOWN") << ", email=" << reader.Get("user", "email", "UNKNOWN") << ", pi=" << reader.GetReal("user", "pi", -1) << ", active=" << reader.GetBoolean("user", "active", true) << "\n"; return 0; } ``` This simple C++ API works fine, but it's not very fully-fledged. I'm not planning to work more on the C++ API at the moment, so if you want a bit more power (for example `GetSections()` and `GetFields()` functions), see these forks: * https://github.com/Blandinium/inih * https://github.com/OSSystems/inih ## Differences from ConfigParser ## Some differences between inih and Python's [ConfigParser](http://docs.python.org/library/configparser.html) standard library module: * INI name=value pairs given above any section headers are treated as valid items with no section (section name is an empty string). In ConfigParser having no section is an error. * Line continuations are handled with leading whitespace on continued lines (like ConfigParser). However, instead of concatenating continued lines together, they are treated as separate values for the same key (unlike ConfigParser). ## Platform-specific notes ## * Windows/Win32 uses UTF-16 filenames natively, so to handle Unicode paths you need to call `_wfopen()` to open a file and then `ini_parse_file()` to parse it; inih does not include `wchar_t` or Unicode handling. ## Meson notes ## * The `meson.build` file is not required to use or compile inih, its main purpose is for distributions. * By default Meson is set up for distro installation, but this behavior can be configured for embedded use cases: * with `-Ddefault_library=static` static libraries are built. * with `-Ddistro_install=false` libraries, headers and pkg-config files won't be installed. * with `-Dwith_INIReader=false` you can disable building the C++ library. * All compile-time options are implemented in Meson as well, you can take a look at [meson_options.txt](https://github.com/benhoyt/inih/blob/master/meson_options.txt) for their definition. These won't work if `distro_install` is set to `true`. * If you want to use inih for programs which may be shipped in a distro, consider linking against the shared libraries. The pkg-config entries are `inih` and `INIReader`. * In case you use inih as a Meson subproject, you can use the `inih_dep` and `INIReader_dep` dependency variables. You might want to set `default_library=static` and `distro_install=false` for the subproject. An official Wrap is provided on [WrapDB](https://wrapdb.mesonbuild.com/inih). * For packagers: if you want to tag the version in the pkg-config file, you will need to do this downstream. Add `version : '',` after the `license` tag in the `project()` function and `version : meson.project_version(),` after the `soversion` tag in both `library()` functions. ## Building from vcpkg ## You can build and install inih using [vcpkg](https://github.com/microsoft/vcpkg/) dependency manager: git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install ./vcpkg install inih The inih port in vcpkg is kept up to date by microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. ## Related links ## * [Conan package for inih](https://github.com/mohamedghita/conan-inih) (Conan is a C/C++ package manager) ================================================ FILE: deps/inih/ini.c ================================================ /* inih -- simple .INI file parser SPDX-License-Identifier: BSD-3-Clause Copyright (C) 2009-2020, Ben Hoyt inih is released under the New BSD license (see LICENSE.txt). Go to the project home page for more info: https://github.com/benhoyt/inih */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #include #include #include #include "ini.h" #if !INI_USE_STACK #if INI_CUSTOM_ALLOCATOR #include void* ini_malloc(size_t size); void ini_free(void* ptr); void* ini_realloc(void* ptr, size_t size); #else #include #define ini_malloc malloc #define ini_free free #define ini_realloc realloc #endif #endif #define MAX_SECTION 50 #define MAX_NAME 50 /* Used by ini_parse_string() to keep track of string parsing state. */ typedef struct { const char* ptr; size_t num_left; } ini_parse_string_ctx; /* Strip whitespace chars off end of given string, in place. Return s. */ static char* rstrip(char* s) { char* p = s + strlen(s); while (p > s && isspace((unsigned char)(*--p))) *p = '\0'; return s; } /* Return pointer to first non-whitespace char in given string. */ static char* lskip(const char* s) { while (*s && isspace((unsigned char)(*s))) s++; return (char*)s; } /* Return pointer to first char (of chars) or inline comment in given string, or pointer to NUL at end of string if neither found. Inline comment must be prefixed by a whitespace character to register as a comment. */ static char* find_chars_or_comment(const char* s, const char* chars) { #if INI_ALLOW_INLINE_COMMENTS int was_space = 0; while (*s && (!chars || !strchr(chars, *s)) && !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { was_space = isspace((unsigned char)(*s)); s++; } #else while (*s && (!chars || !strchr(chars, *s))) { s++; } #endif return (char*)s; } /* Similar to strncpy, but ensures dest (size bytes) is NUL-terminated, and doesn't pad with NULs. */ static char* strncpy0(char* dest, const char* src, size_t size) { /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ size_t i; for (i = 0; i < size - 1 && src[i]; i++) dest[i] = src[i]; dest[i] = '\0'; return dest; } /* See documentation in header file. */ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, void* user) { /* Uses a fair bit of stack (use heap instead if you need to) */ #if INI_USE_STACK char line[INI_MAX_LINE]; int max_line = INI_MAX_LINE; #else char* line; size_t max_line = INI_INITIAL_ALLOC; #endif #if INI_ALLOW_REALLOC && !INI_USE_STACK char* new_line; size_t offset; #endif char section[MAX_SECTION] = ""; char prev_name[MAX_NAME] = ""; char* start; char* end; char* name; char* value; int lineno = 0; int error = 0; #if !INI_USE_STACK line = (char*)ini_malloc(INI_INITIAL_ALLOC); if (!line) { return -2; } #endif #if INI_HANDLER_LINENO #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) #else #define HANDLER(u, s, n, v) handler(u, s, n, v) #endif /* Scan through stream line by line */ while (reader(line, (int)max_line, stream) != NULL) { #if INI_ALLOW_REALLOC && !INI_USE_STACK offset = strlen(line); while (offset == max_line - 1 && line[offset - 1] != '\n') { max_line *= 2; if (max_line > INI_MAX_LINE) max_line = INI_MAX_LINE; new_line = ini_realloc(line, max_line); if (!new_line) { ini_free(line); return -2; } line = new_line; if (reader(line + offset, (int)(max_line - offset), stream) == NULL) break; if (max_line >= INI_MAX_LINE) break; offset += strlen(line + offset); } #endif lineno++; start = line; #if INI_ALLOW_BOM if (lineno == 1 && (unsigned char)start[0] == 0xEF && (unsigned char)start[1] == 0xBB && (unsigned char)start[2] == 0xBF) { start += 3; } #endif start = lskip(rstrip(start)); if (strchr(INI_START_COMMENT_PREFIXES, *start)) { /* Start-of-line comment */ } #if INI_ALLOW_MULTILINE else if (*prev_name && *start && start > line) { /* Non-blank line with leading whitespace, treat as continuation of previous name's value (as per Python configparser). */ if (!HANDLER(user, section, prev_name, start) && !error) error = lineno; } #endif else if (*start == '[') { /* A "[section]" line */ end = find_chars_or_comment(start + 1, "]"); if (*end == ']') { *end = '\0'; strncpy0(section, start + 1, sizeof(section)); *prev_name = '\0'; #if INI_CALL_HANDLER_ON_NEW_SECTION if (!HANDLER(user, section, NULL, NULL) && !error) error = lineno; #endif } else if (!error) { /* No ']' found on section line */ error = lineno; } } else if (*start) { /* Not a comment, must be a name[=:]value pair */ end = find_chars_or_comment(start, "=:"); if (*end == '=' || *end == ':') { *end = '\0'; name = rstrip(start); value = end + 1; #if INI_ALLOW_INLINE_COMMENTS end = find_chars_or_comment(value, NULL); if (*end) *end = '\0'; #endif value = lskip(value); rstrip(value); /* Valid name[=:]value pair found, call handler */ strncpy0(prev_name, name, sizeof(prev_name)); if (!HANDLER(user, section, name, value) && !error) error = lineno; } else if (!error) { /* No '=' or ':' found on name[=:]value line */ #if INI_ALLOW_NO_VALUE *end = '\0'; name = rstrip(start); if (!HANDLER(user, section, name, NULL) && !error) error = lineno; #else error = lineno; #endif } } #if INI_STOP_ON_FIRST_ERROR if (error) break; #endif } #if !INI_USE_STACK ini_free(line); #endif return error; } /* See documentation in header file. */ int ini_parse_file(FILE* file, ini_handler handler, void* user) { return ini_parse_stream((ini_reader)fgets, file, handler, user); } /* See documentation in header file. */ int ini_parse(const char* filename, ini_handler handler, void* user) { FILE* file; int error; file = fopen(filename, "r"); if (!file) return -1; error = ini_parse_file(file, handler, user); fclose(file); return error; } /* An ini_reader function to read the next line from a string buffer. This is the fgets() equivalent used by ini_parse_string(). */ static char* ini_reader_string(char* str, int num, void* stream) { ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; const char* ctx_ptr = ctx->ptr; size_t ctx_num_left = ctx->num_left; char* strp = str; char c; if (ctx_num_left == 0 || num < 2) return NULL; while (num > 1 && ctx_num_left != 0) { c = *ctx_ptr++; ctx_num_left--; *strp++ = c; if (c == '\n') break; num--; } *strp = '\0'; ctx->ptr = ctx_ptr; ctx->num_left = ctx_num_left; return str; } /* See documentation in header file. */ int ini_parse_string(const char* string, ini_handler handler, void* user) { ini_parse_string_ctx ctx; ctx.ptr = string; ctx.num_left = strlen(string); return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, user); } ================================================ FILE: deps/inih/ini.h ================================================ /* inih -- simple .INI file parser SPDX-License-Identifier: BSD-3-Clause Copyright (C) 2009-2020, Ben Hoyt inih is released under the New BSD license (see LICENSE.txt). Go to the project home page for more info: https://github.com/benhoyt/inih */ #ifndef INI_H #define INI_H /* Make this header file easier to include in C++ code */ #ifdef __cplusplus extern "C" { #endif #include /* Nonzero if ini_handler callback should accept lineno parameter. */ #ifndef INI_HANDLER_LINENO #define INI_HANDLER_LINENO 0 #endif /* Typedef for prototype of handler function. */ #if INI_HANDLER_LINENO typedef int (*ini_handler)(void* user, const char* section, const char* name, const char* value, int lineno); #else typedef int (*ini_handler)(void* user, const char* section, const char* name, const char* value); #endif /* Typedef for prototype of fgets-style reader function. */ typedef char* (*ini_reader)(char* str, int num, void* stream); /* Parse given INI-style file. May have [section]s, name=value pairs (whitespace stripped), and comments starting with ';' (semicolon). Section is "" if name=value pair parsed before any section heading. name:value pairs are also supported as a concession to Python's configparser. For each name=value pair parsed, call handler function with given user pointer as well as section, name, and value (data only valid for duration of handler call). Handler should return nonzero on success, zero on error. Returns 0 on success, line number of first error on parse error (doesn't stop on first error), -1 on file open error, or -2 on memory allocation error (only when INI_USE_STACK is zero). */ int ini_parse(const char* filename, ini_handler handler, void* user); /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't close the file when it's finished -- the caller must do that. */ int ini_parse_file(FILE* file, ini_handler handler, void* user); /* Same as ini_parse(), but takes an ini_reader function pointer instead of filename. Used for implementing custom or string-based I/O (see also ini_parse_string). */ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, void* user); /* Same as ini_parse(), but takes a zero-terminated string with the INI data instead of a file. Useful for parsing INI data from a network socket or already in memory. */ int ini_parse_string(const char* string, ini_handler handler, void* user); /* Nonzero to allow multi-line value parsing, in the style of Python's configparser. If allowed, ini_parse() will call the handler with the same name for each subsequent line parsed. */ #ifndef INI_ALLOW_MULTILINE #define INI_ALLOW_MULTILINE 1 #endif /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of the file. See https://github.com/benhoyt/inih/issues/21 */ #ifndef INI_ALLOW_BOM #define INI_ALLOW_BOM 1 #endif /* Chars that begin a start-of-line comment. Per Python configparser, allow both ; and # comments at the start of a line by default. */ #ifndef INI_START_COMMENT_PREFIXES #define INI_START_COMMENT_PREFIXES ";#" #endif /* Nonzero to allow inline comments (with valid inline comment characters specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match Python 3.2+ configparser behaviour. */ #ifndef INI_ALLOW_INLINE_COMMENTS #define INI_ALLOW_INLINE_COMMENTS 1 #endif #ifndef INI_INLINE_COMMENT_PREFIXES #define INI_INLINE_COMMENT_PREFIXES ";" #endif /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ #ifndef INI_USE_STACK #define INI_USE_STACK 1 #endif /* Maximum line length for any line in INI file (stack or heap). Note that this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ #ifndef INI_MAX_LINE #define INI_MAX_LINE 200 #endif /* Nonzero to allow heap line buffer to grow via realloc(), zero for a fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is zero. */ #ifndef INI_ALLOW_REALLOC #define INI_ALLOW_REALLOC 0 #endif /* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK is zero. */ #ifndef INI_INITIAL_ALLOC #define INI_INITIAL_ALLOC 200 #endif /* Stop parsing on first error (default is to keep parsing). */ #ifndef INI_STOP_ON_FIRST_ERROR #define INI_STOP_ON_FIRST_ERROR 0 #endif /* Nonzero to call the handler at the start of each new section (with name and value NULL). Default is to only call the handler on each name=value pair. */ #ifndef INI_CALL_HANDLER_ON_NEW_SECTION #define INI_CALL_HANDLER_ON_NEW_SECTION 0 #endif /* Nonzero to allow a name without a value (no '=' or ':' on the line) and call the handler with value NULL in this case. Default is to treat no-value lines as an error. */ #ifndef INI_ALLOW_NO_VALUE #define INI_ALLOW_NO_VALUE 0 #endif /* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory allocation functions (INI_USE_STACK must also be 0). These functions must have the same signatures as malloc/free/realloc and behave in a similar way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ #ifndef INI_CUSTOM_ALLOCATOR #define INI_CUSTOM_ALLOCATOR 0 #endif #ifdef __cplusplus } #endif #endif /* INI_H */ ================================================ FILE: deps/sol3/CMakeLists.txt ================================================ add_library(sol3 INTERFACE EXCLUDE_FROM_ALL) target_include_directories(sol3 INTERFACE include) target_link_libraries(sol3 INTERFACE lua::lua54) ================================================ FILE: deps/sol3/CONTRIBUTORS.md ================================================ # 🎉 Donators! ♥ 🎉 Thank you to all patrons, donators and contributors who help keep sol3 amazing. - Robert Salvet - Ορφέας Ζαφείρης - 2x Donations! - Michael Wallar - Johannes Schultz - Elias Daler - BECKMANN & EGLE Industrieelektronik GmbH [bue.de](https://www.bue.de/) # 🎉 Patrons! ♥ 🎉 Beyond just a one-time donation, patrons make a continued commitment to help keep sol3 supported and bug-free. Thank you for your patronage! Here are the supporters that wanted to be featured as sol3 contributors. - Joel Falcou - Michael Caisse - Joshua Fisher - Ορφέας Ζαφείρης # Company Patrons / Supporters # Companies who sign up for a long-term support contract or patronage are listed here! They really push forward what's possible with sol3 (and the newer v3)! Please reach out to phdofthehouse@gmail.com if you are interested in a custom solution or a long-term support contract that goes beyond the current release's needs! - Intrepid Control Systems [intrepidcs.com](https://www.intrepidcs.com/) ================================================ FILE: deps/sol3/LICENSE.txt ================================================ The MIT License (MIT) Copyright (c) 2013-2020 Rapptz, ThePhD, and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: deps/sol3/README.md ================================================ ## sol3 (sol2 v3.2.3) [![Linux & Max OSX Build Status](https://travis-ci.org/ThePhD/sol2.svg?branch=develop)](https://travis-ci.org/ThePhD/sol2) [![Windows Build status](https://ci.appveyor.com/api/projects/status/n38suofr21e9uk7h?svg=true)](https://ci.appveyor.com/project/ThePhD/sol2) [![Documentation Status](https://readthedocs.org/projects/sol2/badge/?version=latest)](http://sol2.readthedocs.io/en/latest/?badge=latest) [![Support via Github Sponsors](https://img.shields.io/badge/Github-Become%20a%20Sponsor-ff69b4.svg?style=flat&logo=GitHub)](https://github.com/users/ThePhD/sponsorship) [![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/Soasis) [![Support via Ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Soasis) [![Support via Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.herokuapp.com%2FSoasis)](https://patreon.com/soasis) [![Support via Liberapay](https://img.shields.io/liberapay/patrons/ThePhD.svg)](https://liberapay.com/Soasis/) sol2 is a C++ library binding to Lua. It currently supports all Lua versions 5.1+ (LuaJIT 2.x included). sol2 aims to be easy to use and easy to add to a project. The library is header-only for easy integration with projects. ## Documentation Find it [here](http://sol2.rtfd.io/). A run-through kind of tutorial is [here](http://sol2.readthedocs.io/en/latest/tutorial/all-the-things.html)! The API documentation goes over most cases (particularly, the "api/usertype" and "api/table_proxy" and "api/function" sections) that should still get you off your feet and going, and there's an examples directory [here](https://github.com/ThePhD/sol2/tree/develop/examples) as well. ## Sneak Peek ```cpp #include #include int main() { sol::state lua; int x = 0; lua.set_function("beep", [&x]{ ++x; }); lua.script("beep()"); assert(x == 1); } ``` ```cpp #include #include struct vars { int boop = 0; }; int main() { sol::state lua; lua.new_usertype("vars", "boop", &vars::boop); lua.script("beep = vars.new()\n" "beep.boop = 1"); assert(lua.get("beep").boop == 1); } ``` More examples are given in the examples directory [here](https://github.com/ThePhD/sol2/tree/develop/examples). ## Supporting Please use the buttons above and help this project grow. You can also help out the library by submitting pull requests to fix anything or add anything you think would be helpful! This includes making small, useful examples of something you haven't seen, or fixing typos and bad code in the documentation. ## Presentations "A Sun For the Moon - A Zero-Overhead Lua Abstraction using C++" ThePhD Lua Workshop 2016 - Mashape, San Francisco, CA [Deck](https://github.com/ThePhD/sol2/blob/develop/docs/presentations/2016.10.14%20-%20ThePhD%20-%20No%20Overhead%20C%20Abstraction.pdf) "Wrapping Lua C in C++ - Efficiently, Nicely, and with a Touch of Magic" ThePhD Boston C++ Meetup November 2017 - CiC (Milk Street), Boston, MA [Deck](https://github.com/ThePhD/sol2/blob/develop/docs/presentations/2017.11.08%20-%20ThePhD%20-%20Wrapping%20Lua%20C%20in%20C%2B%2B.pdf) "Biting the CMake Bullet" ThePhD Boston C++ Meetup February 2018 - CiC (Main Street), Cambridge, MA [Deck](https://github.com/ThePhD/sol2/blob/develop/docs/presentations/2018.02.06%20-%20ThePhD%20-%20Biting%20the%20CMake%20Bullet.pdf) "Compile Fast, Run Faster, Scale Forever: A look into the sol2 Library" ThePhD C++Now 2018 - Hudson Commons, Aspen Physics Center, Aspen, Colorado [Deck](https://github.com/ThePhD/sol2/blob/develop/docs/presentations/2018.05.10%20-%20ThePhD%20-%20Compile%20Fast%2C%20Run%20Faster%2C%20Scale%20Forever.pdf) "Scripting at the Speed of Thought: Using Lua in C++ with sol3" ThePhD CppCon 2018 - 404 Keystone, Meydenbauer Center, Aspen, Colorado [Deck](https://github.com/ThePhD/sol2/blob/develop/docs/presentations/2018.09.28%20-%20ThePhD%20-%20Scripting%20at%20the%20Speed%20of%20Thought.pdf) "The Plan for Tomorrow: Compile-Time Extension Points in C++" ThePhD C++Now 2019 - Flug Auditorium, Aspen Physics Center, Aspen, Colorado [Deck](https://github.com/ThePhD/sol2/blob/develop/docs/presentations/2019.05.10%20-%20ThePhD%20-%20The%20Plan%20for%20Tomorrow%20-%20Compile-Time%20Extension%20Points%20in%20C%2b%2b.pdf) ## Features - [Fastest in the land](http://sol2.readthedocs.io/en/latest/benchmarks.html) (see: sol3 bar in graph). - Supports retrieval and setting of multiple types including: * `std::string`, `std::wstring`, `std::u16string` and `std::u32string` support (and for views). * understands and works with containers such as `std::map/unordered_map`, c-style arrays, vectors, non-standard custom containers and more. * user-defined types, with or **without** registering that type * `std::unique_ptr`, `std::shared_ptr`, and optional support of other pointer types like `boost::shared_ptr`. * custom `optional` that works with references, and support for the inferior `std::optional`. * C++17 support for variants and similar new types. - Lambda, function, and member function bindings are supported. - Intermediate type for checking if a variable exists. - Simple API that completely abstracts away the C stack API, including `protected_function` with the ability to use an error-handling function. - `operator[]`-style manipulation of tables - C++ type representations in Lua userdata as `usertype`s with guaranteed cleanup. - Customization points to allow your C++ objects to be pushed and retrieved from Lua as multiple consecutive objects, or anything else you desire! - Overloaded function calls: `my_function(1); my_function("Hello")` in the same Lua script route to different function calls based on parameters - Support for tables, nested tables, table iteration with `table.for_each` / `begin()` and `end()` iterators. - Zero string overhead for usertype function lookup. ## Supported Compilers sol2 makes use of C++17 features. GCC 7.x.x and Clang 3.9.x (with `-std=c++1z` and appropriate standard library) or higher should be able to compile without problems. However, the officially supported and CI-tested compilers are: - GCC 7.x.x+ (MinGW 7.x.x+) - Clang 3.9.x+ - Visual Studio 2017 Community (Visual C++ 15.0)+ Please make sure you use the `-std=c++2a`, `-std=c++1z`, `-std=c++17` or better standard flags (some of these flags are the defaults in later versions of GCC, such as 7+ and better). If you would like support for an older compiler (at the cost of some features), use the latest tagged sol2 branch. If you would like support for an even older compiler, feel free to contact me for a Custom Solution. sol3 is checked by-hand for other platforms as well, including Android-based builds with GCC and iOS-based builds out of XCode with Apple-clang. It should work on both of these platforms, so long as you have the proper standards flags. ## Creating a single header You can grab a single header (and the single forward header) out of the library [here](https://github.com/ThePhD/sol2/tree/develop/single). For stable version, check the releases tab on GitHub for a provided single header file for maximum ease of use. A script called [`single.py`](https://github.com/ThePhD/sol2/blob/develop/single/single.py) is provided in the repository if there's some bleeding edge change that hasn't been published on the releases page. You can run this script to create a single file version of the library so you can only include that part of it. Check `single.py --help` for more info. If you use CMake, you can also configure and generate a project that will generate the `sol2_single_header` for you. You can also include the project using CMake. Run CMake for more details. Thanks @Nava2, @alkino, @mrgreywater and others for help with making the CMake build a reality. ## Running the Tests Testing on Travis-CI and Appveyor use CMake. You can generate the tests by running CMake and configuring `SOL2_TESTS`, `SOL2_TESTS_SINGLE`, `SOL2_TESTS_EXAMPLES`, and `SOL2_EXAMPLES` to be on. Make sure `SOL2_SINGLE` is also on. You will need any flavor of python3 and an available compiler. The testing suite will build its own version of Lua and LuaJIT, so you do not have to provide one (you may provide one with the `LUA_LOCAL_DIR` variable). ## License sol2 is distributed with an MIT License. You can see LICENSE.txt for more info. If you need a custom solution, feel free to contact me. ================================================ FILE: deps/sol3/include/sol/config.hpp ================================================ // The MIT License (MIT) // Copyright (c) 2013-2020 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. // Generated 2020-10-15 05:19:08.645208 UTC // This header was generated with sol v3.2.3 (revision e5e6466e) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_CONFIG_HPP #define SOL_SINGLE_CONFIG_HPP // beginning of sol/config.hpp /* Base, empty configuration file! To override, place a file in your include paths of the form: . (your include path here) | sol (directory, or equivalent) | config.hpp (your config.hpp file) So that when sol2 includes the file #include it gives you the configuration values you desire. Configuration values can be seen in the safety.rst of the doc/src, or at https://sol2.readthedocs.io/en/latest/safety.html ! You can also pass them through the build system, or the command line options of your compiler. */ // end of sol/config.hpp #endif // SOL_SINGLE_CONFIG_HPP ================================================ FILE: deps/sol3/include/sol/forward.hpp ================================================ // The MIT License (MIT) // Copyright (c) 2013-2020 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. // Generated 2020-10-15 05:19:08.633139 UTC // This header was generated with sol v3.2.3 (revision e5e6466e) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP #define SOL_SINGLE_INCLUDE_FORWARD_HPP // beginning of sol/forward.hpp #ifndef SOL_FORWARD_HPP #define SOL_FORWARD_HPP // beginning of sol/version.hpp #include #include #define SOL_VERSION_MAJOR 3 #define SOL_VERSION_MINOR 2 #define SOL_VERSION_PATCH 3 #define SOL_VERSION_STRING "3.2.3" #define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH)) #define SOL_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0) #define SOL_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0) #define SOL_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3) #define SOL_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0) #define SOL_ON | #define SOL_OFF ^ #define SOL_DEFAULT_ON + #define SOL_DEFAULT_OFF - #if defined(_MSC_VER) #define SOL_COMPILER_CLANG_I_ SOL_OFF #define SOL_COMPILER_GCC_I_ SOL_OFF #define SOL_COMPILER_EDG_I_ SOL_OFF #define SOL_COMPILER_VCXX_I_ SOL_ON #elif defined(__clang__) #define SOL_COMPILER_CLANG_I_ SOL_ON #define SOL_COMPILER_GCC_I_ SOL_OFF #define SOL_COMPILER_EDG_I_ SOL_OFF #define SOL_COMPILER_VCXX_I_ SOL_OFF #elif defined(__GNUC__) #define SOL_COMPILER_CLANG_I_ SOL_OFF #define SOL_COMPILER_GCC_I_ SOL_ON #define SOL_COMPILER_EDG_I_ SOL_OFF #define SOL_COMPILER_VCXX_I_ SOL_OFF #else #define SOL_COMPILER_CLANG_I_ SOL_OFF #define SOL_COMPILER_GCC_I_ SOL_OFF #define SOL_COMPILER_EDG_I_ SOL_OFF #define SOL_COMPILER_VCXX_I_ SOL_OFF #endif #if defined(__MINGW32__) #define SOL_COMPILER_FRONTEND_MINGW_I_ SOL_ON #else #define SOL_COMPILER_FRONTEND_MINGW_I_ SOL_OFF #endif #if SIZE_MAX <= 0xFFFFULL #define SOL_PLATFORM_X16_I_ SOL_ON #define SOL_PLATFORM_X86_I_ SOL_OFF #define SOL_PLATFORM_X64_I_ SOL_OFF #elif SIZE_MAX <= 0xFFFFFFFFULL #define SOL_PLATFORM_X16_I_ SOL_OFF #define SOL_PLATFORM_X86_I_ SOL_ON #define SOL_PLATFORM_X64_I_ SOL_OFF #else #define SOL_PLATFORM_X16_I_ SOL_OFF #define SOL_PLATFORM_X86_I_ SOL_OFF #define SOL_PLATFORM_X64_I_ SOL_ON #endif #define SOL_PLATFORM_ARM32_I_ SOL_OFF #define SOL_PLATFORM_ARM64_I_ SOL_OFF #if defined(_WIN32) #define SOL_PLATFORM_WINDOWS_I_ SOL_ON #else #define SOL_PLATFORM_WINDOWS_I_ SOL_OFF #endif #if defined(__APPLE__) #define SOL_PLATFORM_APPLE_I_ SOL_ON #else #define SOL_PLATFORM_APPLE_I_ SOL_OFF #endif #if defined(__unix__) #define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON #else #define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF #endif #if defined(__linux__) #define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON #else #define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF #endif #define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF #define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF #if defined(SOL_IN_DEBUG_DETECTED) #if SOL_IN_DEBUG_DETECTED != 0 #define SOL_DEBUG_BUILD_I_ SOL_ON #else #define SOL_DEBUG_BUILD_I_ SOL_OFF #endif #elif !defined(NDEBUG) #if SOL_IS_ON(SOL_COMPILER_VCXX_I_) && defined(_DEBUG) #define SOL_DEBUG_BUILD_I_ SOL_ON #elif (SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_)) && !defined(__OPTIMIZE__) #define SOL_DEBUG_BUILD_I_ SOL_ON #else #define SOL_DEBUG_BUILD_I_ SOL_OFF #endif #else #define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF #endif // We are in a debug mode of some sort #if defined(SOL_NO_EXCEPTIONS) #if (SOL_NO_EXCEPTIONS != 0) #define SOL_EXCEPTIONS_I_ SOL_OFF #else #define SOL_EXCEPTIONS_I_ SOL_ON #endif #elif SOL_IS_ON(SOL_COMPILER_VCXX_I_) #if !defined(_CPPUNWIND) #define SOL_EXCEPTIONS_I_ SOL_OFF #else #define SOL_EXCEPTIONS_I_ SOL_ON #endif #elif SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_) #if !defined(__EXCEPTIONS) #define SOL_EXCEPTIONS_I_ SOL_OFF #else #define SOL_EXCEPTIONS_I_ SOL_ON #endif #else #define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON #endif #if defined(SOL_NO_RTTI) #if (SOL_NO_RTTI != 0) #define SOL_RTTI_I_ SOL_OFF #else #define SOL_RTTI_I_ SOL_ON #endif #elif SOL_IS_ON(SOL_COMPILER_VCXX_I_) #if !defined(_CPPRTTI) #define SOL_RTTI_I_ SOL_OFF #else #define SOL_RTTI_I_ SOL_ON #endif #elif SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_) #if !defined(__GXX_RTTI) #define SOL_RTTI_I_ SOL_OFF #else #define SOL_RTTI_I_ SOL_ON #endif #else #define SOL_RTTI_I_ SOL_DEFAULT_ON #endif #if defined(SOL_NO_THREAD_LOCAL) #if SOL_NO_THREAD_LOCAL != 0 #define SOL_USE_THREAD_LOCAL_I_ SOL_OFF #else #define SOL_USE_THREAD_LOCAL_I_ SOL_ON #endif #else #define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON #endif // thread_local keyword is bjorked on some platforms #if defined(SOL_ALL_SAFETIES_ON) #if SOL_ALL_SAFETIES_ON != 0 #define SOL_ALL_SAFETIES_ON_I_ SOL_ON #else #define SOL_ALL_SAFETIES_ON_I_ SOL_FF #endif #else #define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF #endif #if defined(SOL_SAFE_GETTER) #if SOL_SAFE_GETTER != 0 #define SOL_SAFE_GETTER_I_ SOL_ON #else #define SOL_SAFE_GETTER_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_GETTER_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_USERTYPE) #if SOL_SAFE_USERTYPE != 0 #define SOL_SAFE_USERTYPE_I_ SOL_ON #else #define SOL_SAFE_USERTYPE_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_USERTYPE_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_REFERENCES) #if SOL_SAFE_REFERENCES != 0 #define SOL_SAFE_REFERENCES_I_ SOL_ON #else #define SOL_SAFE_REFERENCES_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_REFERENCES_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_FUNCTIONS) #if SOL_SAFE_FUNCTIONS != 0 #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON #else #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF #endif #elif defined (SOL_SAFE_FUNCTION_OBJECTS) #if SOL_SAFE_FUNCTION_OBJECTS != 0 #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON #else #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_FUNCTION_CALLS) #if SOL_SAFE_FUNCTION_CALLS != 0 #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON #else #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_PROXIES) #if SOL_SAFE_PROXIES != 0 #define SOL_SAFE_PROXIES_I_ SOL_ON #else #define SOL_SAFE_PROXIES_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_PROXIES_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_NUMERICS) #if SOL_SAFE_NUMERICS != 0 #define SOL_SAFE_NUMERICS_I_ SOL_ON #else #define SOL_SAFE_NUMERICS_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_NUMERICS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_STACK_CHECK) #if SOL_SAFE_STACK_CHECK != 0 #define SOL_SAFE_STACK_CHECK_I_ SOL_ON #else #define SOL_SAFE_STACK_CHECK_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_STACK_CHECK_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_NO_CHECK_NUMBER_PRECISION) #if SOL_NO_CHECK_NUMBER_PRECISION != 0 #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF #else #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON #endif #elif defined(SOL_NO_CHECKING_NUMBER_PRECISION) #if SOL_NO_CHECKING_NUMBER_PRECISION != 0 #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF #else #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON #elif SOL_IS_ON(SOL_SAFE_NUMERICS_I_) #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON #else #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_STRINGS_ARE_NUMBERS) #if (SOL_STRINGS_ARE_NUMBERS != 0) #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON #else #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF #endif #else #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF #endif #if defined(SOL_ENABLE_INTEROP) #if SOL_ENABLE_INTEROP != 0 #define SOL_USE_INTEROP_I_ SOL_ON #else #define SOL_USE_INTEROP_I_ SOL_OFF #endif #elif defined(SOL_USE_INTEROP) #if SOL_USE_INTEROP != 0 #define SOL_USE_INTEROP_I_ SOL_ON #else #define SOL_USE_INTEROP_I_ SOL_OFF #endif #else #define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF #endif #if defined(SOL_NO_NIL) #if (SOL_NO_NIL != 0) #define SOL_NIL_I_ SOL_OFF #else #define SOL_NIL_I_ SOL_ON #endif #elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil) #define SOL_NIL_I_ SOL_DEFAULT_OFF #else #define SOL_NIL_I_ SOL_DEFAULT_ON #endif #if defined(SOL_USERTYPE_TYPE_BINDING_INFO) #if (SOL_USERTYPE_TYPE_BINDING_INFO != 0) #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON #else #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF #endif #else #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON #endif // We should generate a my_type.__type table with lots of class information for usertypes #if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT) #if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0) #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON #else #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF #endif #elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES) #if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0) #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON #else #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF #endif #else #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON #endif // make is_automagical on/off by default #if defined(SOL_STD_VARIANT) #if (SOL_STD_VARIANT != 0) #define SOL_STD_VARIANT_I_ SOL_ON #else #define SOL_STD_VARIANT_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_COMPILER_CLANG_I_) && SOL_IS_ON(SOL_PLATFORM_APPLE_I_) #if defined(__has_include) #if __has_include() #define SOL_STD_VARIANT_I_ SOL_ON #else #define SOL_STD_VARIANT_I_ SOL_OFF #endif #else #define SOL_STD_VARIANT_I_ SOL_OFF #endif #else #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON #endif #endif // make is_automagical on/off by default #if defined(SOL_NOEXCEPT_FUNCTION_TYPE) #if (SOL_NOEXCEPT_FUNCTION_TYPE != 0) #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON #else #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF #endif #else #if defined(__cpp_noexcept_function_type) #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON #elif SOL_IS_ON(SOL_COMPILER_VCXX_I_) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L)) // There is a bug in the VC++ compiler?? // on /std:c++latest under x86 conditions (VS 15.5.2), // compiler errors are tossed for noexcept markings being on function types // that are identical in every other way to their non-noexcept marked types function types... // VS 2019: There is absolutely a bug. #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF #else #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON #endif #endif // noexcept is part of a function's type #if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && SOL_STACK_STRING_OPTIMIZATION_SIZE > 0 #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE #else #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024 #endif #if defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0 #define SOL_ID_SIZE_I_ SOL_ID_SIZE #else #define SOL_ID_SIZE_I_ 512 #endif #if defined(LUA_IDSIZE) && LUA_IDSIZE > 0 #define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE #elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0 #define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE #else #define SOL_FILE_ID_SIZE_I_ 2048 #endif #if defined(SOL_PRINT_ERRORS) #if (SOL_PRINT_ERRORS != 0) #define SOL_PRINT_ERRORS_I_ SOL_ON #else #define SOL_PRINT_ERRORS_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_PRINT_ERRORS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON #else #define SOL_PRINT_ERRORS_I_ SOL_OFF #endif #endif #if defined(SOL_DEFAULT_PASS_ON_ERROR) #if (SOL_DEFAULT_PASS_ON_ERROR != 0) #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON #else #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF #endif #else #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_OFF #endif #if defined(SOL_USING_CXX_LUA) #if (SOL_USING_CXX_LUA != 0) #define SOL_USE_CXX_LUA_I_ SOL_ON #else #define SOL_USE_CXX_LUA_I_ SOL_OFF #endif #elif defined(SOL_USE_CXX_LUA) #if (SOL_USE_CXX_LUA != 0) #define SOL_USE_CXX_LUA_I_ SOL_ON #else #define SOL_USE_CXX_LUA_I_ SOL_OFF #endif #else #define SOL_USE_CXX_LUA_I_ SOL_OFF #endif #if defined(SOL_USING_CXX_LUAJIT) #if (SOL_USING_CXX_LUA != 0) #define SOL_USE_CXX_LUAJIT_I_ SOL_ON #else #define SOL_USE_CXX_LUAJIT_I_ SOL_OFF #endif #elif defined(SOL_USE_CXX_LUAJIT) #if (SOL_USE_CXX_LUA != 0) #define SOL_USE_CXX_LUAJIT_I_ SOL_ON #else #define SOL_USE_CXX_LUAJIT_I_ SOL_OFF #endif #else #define SOL_USE_CXX_LUAJIT_I_ SOL_OFF #endif #if defined(SOL_NO_LUA_HPP) #if (SOL_NO_LUA_HPP != 0) #define SOL_USE_LUA_HPP_I_ SOL_OFF #else #define SOL_USE_LUA_HPP_I_ SOL_ON #endif #elif defined(SOL_USING_CXX_LUA) #define SOL_USE_LUA_HPP_I_ SOL_OFF #elif defined(__has_include) #if __has_include() #define SOL_USE_LUA_HPP_I_ SOL_ON #else #define SOL_USE_LUA_HPP_I_ SOL_OFF #endif #else #define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON #endif #if defined(SOL_CONTAINERS_START) #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START #elif defined(SOL_CONTAINERS_START_INDEX) #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX #elif defined(SOL_CONTAINER_START_INDEX) #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX #else #define SOL_CONTAINER_START_INDEX_I_ 1 #endif #if defined (SOL_NO_MEMORY_ALIGNMENT) #if (SOL_NO_MEMORY_ALIGNMENT != 0) #define SOL_ALIGN_MEMORY_I_ SOL_OFF #else #define SOL_ALIGN_MEMORY_I_ SOL_ON #endif #else #define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON #endif #if defined(SOL_USE_BOOST) #if (SOL_USE_BOOST != 0) #define SOL_USE_BOOST_I_ SOL_ON #else #define SOL_USE_BOOST_I_ SOL_OFF #endif #else #define SOL_USE_BOOST_I_ SOL_OFF #endif #if defined(SOL_USE_UNSAFE_BASE_LOOKUP) #if (SOL_USE_UNSAFE_BASE_LOOKUP != 0) #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON #else #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF #endif #else #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF #endif #if defined(SOL_INSIDE_UNREAL) #if (SOL_INSIDE_UNREAL != 0) #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON #else #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF #endif #else #if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER) #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON #else #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_NO_COMPAT) #if (SOL_NO_COMPAT != 0) #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF #else #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON #endif #else #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON #endif #if defined(SOL_GET_FUNCTION_POINTER_UNSAFE) #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0) #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON #else #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF #endif #else #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF #endif #if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6) // MinGW is off its rocker in some places... #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON #else #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF #endif // end of sol/version.hpp #include #include #include #if SOL_IS_ON(SOL_USE_CXX_LUA_I_) || SOL_IS_ON(SOL_USE_CXX_LUAJIT_I_) struct lua_State; #else extern "C" { struct lua_State; } #endif // C++ Mangling for Lua vs. Not namespace sol { enum class type; class stateless_reference; template class basic_reference; using reference = basic_reference; using main_reference = basic_reference; class stateless_stack_reference; class stack_reference; template class basic_bytecode; struct lua_value; struct proxy_base_tag; template struct proxy_base; template struct table_proxy; template class basic_table_core; template using table_core = basic_table_core; template using main_table_core = basic_table_core; template using stack_table_core = basic_table_core; template using basic_table = basic_table_core; using table = table_core; using global_table = table_core; using main_table = main_table_core; using main_global_table = main_table_core; using stack_table = stack_table_core; using stack_global_table = stack_table_core; template struct basic_lua_table; using lua_table = basic_lua_table; using stack_lua_table = basic_lua_table; template class basic_usertype; template using usertype = basic_usertype; template using stack_usertype = basic_usertype; template class basic_metatable; using metatable = basic_metatable; using stack_metatable = basic_metatable; template struct basic_environment; using environment = basic_environment; using main_environment = basic_environment; using stack_environment = basic_environment; template class basic_function; template class basic_protected_function; using unsafe_function = basic_function; using safe_function = basic_protected_function; using main_unsafe_function = basic_function; using main_safe_function = basic_protected_function; using stack_unsafe_function = basic_function; using stack_safe_function = basic_protected_function; using stack_aligned_unsafe_function = basic_function; using stack_aligned_safe_function = basic_protected_function; using protected_function = safe_function; using main_protected_function = main_safe_function; using stack_protected_function = stack_safe_function; using stack_aligned_protected_function = stack_aligned_safe_function; #if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS_I_) using function = protected_function; using main_function = main_protected_function; using stack_function = stack_protected_function; using stack_aligned_function = stack_aligned_safe_function; #else using function = unsafe_function; using main_function = main_unsafe_function; using stack_function = stack_unsafe_function; using stack_aligned_function = stack_aligned_unsafe_function; #endif using stack_aligned_stack_handler_function = basic_protected_function; struct unsafe_function_result; struct protected_function_result; using safe_function_result = protected_function_result; #if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS_I_) using function_result = safe_function_result; #else using function_result = unsafe_function_result; #endif template class basic_object_base; template class basic_object; template class basic_userdata; template class basic_lightuserdata; template class basic_coroutine; template class basic_thread; using object = basic_object; using userdata = basic_userdata; using lightuserdata = basic_lightuserdata; using thread = basic_thread; using coroutine = basic_coroutine; using main_object = basic_object; using main_userdata = basic_userdata; using main_lightuserdata = basic_lightuserdata; using main_coroutine = basic_coroutine; using stack_object = basic_object; using stack_userdata = basic_userdata; using stack_lightuserdata = basic_lightuserdata; using stack_thread = basic_thread; using stack_coroutine = basic_coroutine; struct stack_proxy_base; struct stack_proxy; struct variadic_args; struct variadic_results; struct stack_count; struct this_state; struct this_main_state; struct this_environment; class state_view; class state; template struct as_table_t; template struct as_container_t; template struct nested; template struct light; template struct user; template struct as_args_t; template struct protect_t; template struct policy_wrapper; template struct usertype_traits; template struct unique_usertype_traits; template struct types { typedef std::make_index_sequence indices; static constexpr std::size_t size() { return sizeof...(Args); } }; template struct derive : std::false_type { typedef types<> type; }; template struct base : std::false_type { typedef types<> type; }; template struct weak_derive { static bool value; }; template bool weak_derive::value = false; namespace stack { struct record; } #if SOL_IS_OFF(SOL_USE_BOOST_I_) template class optional; template class optional; #endif using check_handler_type = int(lua_State*, int, type, type, const char*); } // namespace sol #define SOL_BASE_CLASSES(T, ...) \ namespace sol { \ template <> \ struct base : std::true_type { \ typedef ::sol::types<__VA_ARGS__> type; \ }; \ } \ void a_sol3_detail_function_decl_please_no_collide() #define SOL_DERIVED_CLASSES(T, ...) \ namespace sol { \ template <> \ struct derive : std::true_type { \ typedef ::sol::types<__VA_ARGS__> type; \ }; \ } \ void a_sol3_detail_function_decl_please_no_collide() #endif // SOL_FORWARD_HPP // end of sol/forward.hpp #endif // SOL_SINGLE_INCLUDE_FORWARD_HPP ================================================ FILE: deps/sol3/include/sol/sol.hpp ================================================ // The MIT License (MIT) // Copyright (c) 2013-2020 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. // Generated 2020-10-15 05:19:08.128139 UTC // This header was generated with sol v3.2.3 (revision e5e6466e) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP #define SOL_SINGLE_INCLUDE_HPP // beginning of sol/sol.hpp #ifndef SOL_HPP #define SOL_HPP // beginning of sol/version.hpp #include #include #define SOL_VERSION_MAJOR 3 #define SOL_VERSION_MINOR 2 #define SOL_VERSION_PATCH 3 #define SOL_VERSION_STRING "3.2.3" #define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH)) #define SOL_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0) #define SOL_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0) #define SOL_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3) #define SOL_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0) #define SOL_ON | #define SOL_OFF ^ #define SOL_DEFAULT_ON + #define SOL_DEFAULT_OFF - #if defined(_MSC_VER) #define SOL_COMPILER_CLANG_I_ SOL_OFF #define SOL_COMPILER_GCC_I_ SOL_OFF #define SOL_COMPILER_EDG_I_ SOL_OFF #define SOL_COMPILER_VCXX_I_ SOL_ON #elif defined(__clang__) #define SOL_COMPILER_CLANG_I_ SOL_ON #define SOL_COMPILER_GCC_I_ SOL_OFF #define SOL_COMPILER_EDG_I_ SOL_OFF #define SOL_COMPILER_VCXX_I_ SOL_OFF #elif defined(__GNUC__) #define SOL_COMPILER_CLANG_I_ SOL_OFF #define SOL_COMPILER_GCC_I_ SOL_ON #define SOL_COMPILER_EDG_I_ SOL_OFF #define SOL_COMPILER_VCXX_I_ SOL_OFF #else #define SOL_COMPILER_CLANG_I_ SOL_OFF #define SOL_COMPILER_GCC_I_ SOL_OFF #define SOL_COMPILER_EDG_I_ SOL_OFF #define SOL_COMPILER_VCXX_I_ SOL_OFF #endif #if defined(__MINGW32__) #define SOL_COMPILER_FRONTEND_MINGW_I_ SOL_ON #else #define SOL_COMPILER_FRONTEND_MINGW_I_ SOL_OFF #endif #if SIZE_MAX <= 0xFFFFULL #define SOL_PLATFORM_X16_I_ SOL_ON #define SOL_PLATFORM_X86_I_ SOL_OFF #define SOL_PLATFORM_X64_I_ SOL_OFF #elif SIZE_MAX <= 0xFFFFFFFFULL #define SOL_PLATFORM_X16_I_ SOL_OFF #define SOL_PLATFORM_X86_I_ SOL_ON #define SOL_PLATFORM_X64_I_ SOL_OFF #else #define SOL_PLATFORM_X16_I_ SOL_OFF #define SOL_PLATFORM_X86_I_ SOL_OFF #define SOL_PLATFORM_X64_I_ SOL_ON #endif #define SOL_PLATFORM_ARM32_I_ SOL_OFF #define SOL_PLATFORM_ARM64_I_ SOL_OFF #if defined(_WIN32) #define SOL_PLATFORM_WINDOWS_I_ SOL_ON #else #define SOL_PLATFORM_WINDOWS_I_ SOL_OFF #endif #if defined(__APPLE__) #define SOL_PLATFORM_APPLE_I_ SOL_ON #else #define SOL_PLATFORM_APPLE_I_ SOL_OFF #endif #if defined(__unix__) #define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON #else #define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF #endif #if defined(__linux__) #define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON #else #define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF #endif #define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF #define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF #if defined(SOL_IN_DEBUG_DETECTED) #if SOL_IN_DEBUG_DETECTED != 0 #define SOL_DEBUG_BUILD_I_ SOL_ON #else #define SOL_DEBUG_BUILD_I_ SOL_OFF #endif #elif !defined(NDEBUG) #if SOL_IS_ON(SOL_COMPILER_VCXX_I_) && defined(_DEBUG) #define SOL_DEBUG_BUILD_I_ SOL_ON #elif (SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_)) && !defined(__OPTIMIZE__) #define SOL_DEBUG_BUILD_I_ SOL_ON #else #define SOL_DEBUG_BUILD_I_ SOL_OFF #endif #else #define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF #endif // We are in a debug mode of some sort #if defined(SOL_NO_EXCEPTIONS) #if (SOL_NO_EXCEPTIONS != 0) #define SOL_EXCEPTIONS_I_ SOL_OFF #else #define SOL_EXCEPTIONS_I_ SOL_ON #endif #elif SOL_IS_ON(SOL_COMPILER_VCXX_I_) #if !defined(_CPPUNWIND) #define SOL_EXCEPTIONS_I_ SOL_OFF #else #define SOL_EXCEPTIONS_I_ SOL_ON #endif #elif SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_) #if !defined(__EXCEPTIONS) #define SOL_EXCEPTIONS_I_ SOL_OFF #else #define SOL_EXCEPTIONS_I_ SOL_ON #endif #else #define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON #endif #if defined(SOL_NO_RTTI) #if (SOL_NO_RTTI != 0) #define SOL_RTTI_I_ SOL_OFF #else #define SOL_RTTI_I_ SOL_ON #endif #elif SOL_IS_ON(SOL_COMPILER_VCXX_I_) #if !defined(_CPPRTTI) #define SOL_RTTI_I_ SOL_OFF #else #define SOL_RTTI_I_ SOL_ON #endif #elif SOL_IS_ON(SOL_COMPILER_CLANG_I_) || SOL_IS_ON(SOL_COMPILER_GCC_I_) #if !defined(__GXX_RTTI) #define SOL_RTTI_I_ SOL_OFF #else #define SOL_RTTI_I_ SOL_ON #endif #else #define SOL_RTTI_I_ SOL_DEFAULT_ON #endif #if defined(SOL_NO_THREAD_LOCAL) #if SOL_NO_THREAD_LOCAL != 0 #define SOL_USE_THREAD_LOCAL_I_ SOL_OFF #else #define SOL_USE_THREAD_LOCAL_I_ SOL_ON #endif #else #define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON #endif // thread_local keyword is bjorked on some platforms #if defined(SOL_ALL_SAFETIES_ON) #if SOL_ALL_SAFETIES_ON != 0 #define SOL_ALL_SAFETIES_ON_I_ SOL_ON #else #define SOL_ALL_SAFETIES_ON_I_ SOL_FF #endif #else #define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF #endif #if defined(SOL_SAFE_GETTER) #if SOL_SAFE_GETTER != 0 #define SOL_SAFE_GETTER_I_ SOL_ON #else #define SOL_SAFE_GETTER_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_GETTER_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_USERTYPE) #if SOL_SAFE_USERTYPE != 0 #define SOL_SAFE_USERTYPE_I_ SOL_ON #else #define SOL_SAFE_USERTYPE_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_USERTYPE_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_REFERENCES) #if SOL_SAFE_REFERENCES != 0 #define SOL_SAFE_REFERENCES_I_ SOL_ON #else #define SOL_SAFE_REFERENCES_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_REFERENCES_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_FUNCTIONS) #if SOL_SAFE_FUNCTIONS != 0 #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON #else #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF #endif #elif defined (SOL_SAFE_FUNCTION_OBJECTS) #if SOL_SAFE_FUNCTION_OBJECTS != 0 #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON #else #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_FUNCTION_CALLS) #if SOL_SAFE_FUNCTION_CALLS != 0 #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON #else #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_PROXIES) #if SOL_SAFE_PROXIES != 0 #define SOL_SAFE_PROXIES_I_ SOL_ON #else #define SOL_SAFE_PROXIES_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_PROXIES_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_NUMERICS) #if SOL_SAFE_NUMERICS != 0 #define SOL_SAFE_NUMERICS_I_ SOL_ON #else #define SOL_SAFE_NUMERICS_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_NUMERICS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_SAFE_STACK_CHECK) #if SOL_SAFE_STACK_CHECK != 0 #define SOL_SAFE_STACK_CHECK_I_ SOL_ON #else #define SOL_SAFE_STACK_CHECK_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_SAFE_STACK_CHECK_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON #else #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_NO_CHECK_NUMBER_PRECISION) #if SOL_NO_CHECK_NUMBER_PRECISION != 0 #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF #else #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON #endif #elif defined(SOL_NO_CHECKING_NUMBER_PRECISION) #if SOL_NO_CHECKING_NUMBER_PRECISION != 0 #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF #else #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON #elif SOL_IS_ON(SOL_SAFE_NUMERICS_I_) #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON #else #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_STRINGS_ARE_NUMBERS) #if (SOL_STRINGS_ARE_NUMBERS != 0) #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON #else #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF #endif #else #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF #endif #if defined(SOL_ENABLE_INTEROP) #if SOL_ENABLE_INTEROP != 0 #define SOL_USE_INTEROP_I_ SOL_ON #else #define SOL_USE_INTEROP_I_ SOL_OFF #endif #elif defined(SOL_USE_INTEROP) #if SOL_USE_INTEROP != 0 #define SOL_USE_INTEROP_I_ SOL_ON #else #define SOL_USE_INTEROP_I_ SOL_OFF #endif #else #define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF #endif #if defined(SOL_NO_NIL) #if (SOL_NO_NIL != 0) #define SOL_NIL_I_ SOL_OFF #else #define SOL_NIL_I_ SOL_ON #endif #elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil) #define SOL_NIL_I_ SOL_DEFAULT_OFF #else #define SOL_NIL_I_ SOL_DEFAULT_ON #endif #if defined(SOL_USERTYPE_TYPE_BINDING_INFO) #if (SOL_USERTYPE_TYPE_BINDING_INFO != 0) #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON #else #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF #endif #else #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON #endif // We should generate a my_type.__type table with lots of class information for usertypes #if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT) #if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0) #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON #else #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF #endif #elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES) #if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0) #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON #else #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF #endif #else #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON #endif // make is_automagical on/off by default #if defined(SOL_STD_VARIANT) #if (SOL_STD_VARIANT != 0) #define SOL_STD_VARIANT_I_ SOL_ON #else #define SOL_STD_VARIANT_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_COMPILER_CLANG_I_) && SOL_IS_ON(SOL_PLATFORM_APPLE_I_) #if defined(__has_include) #if __has_include() #define SOL_STD_VARIANT_I_ SOL_ON #else #define SOL_STD_VARIANT_I_ SOL_OFF #endif #else #define SOL_STD_VARIANT_I_ SOL_OFF #endif #else #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON #endif #endif // make is_automagical on/off by default #if defined(SOL_NOEXCEPT_FUNCTION_TYPE) #if (SOL_NOEXCEPT_FUNCTION_TYPE != 0) #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON #else #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF #endif #else #if defined(__cpp_noexcept_function_type) #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON #elif SOL_IS_ON(SOL_COMPILER_VCXX_I_) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L)) // There is a bug in the VC++ compiler?? // on /std:c++latest under x86 conditions (VS 15.5.2), // compiler errors are tossed for noexcept markings being on function types // that are identical in every other way to their non-noexcept marked types function types... // VS 2019: There is absolutely a bug. #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF #else #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON #endif #endif // noexcept is part of a function's type #if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && SOL_STACK_STRING_OPTIMIZATION_SIZE > 0 #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE #else #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024 #endif #if defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0 #define SOL_ID_SIZE_I_ SOL_ID_SIZE #else #define SOL_ID_SIZE_I_ 512 #endif #if defined(LUA_IDSIZE) && LUA_IDSIZE > 0 #define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE #elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0 #define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE #else #define SOL_FILE_ID_SIZE_I_ 2048 #endif #if defined(SOL_PRINT_ERRORS) #if (SOL_PRINT_ERRORS != 0) #define SOL_PRINT_ERRORS_I_ SOL_ON #else #define SOL_PRINT_ERRORS_I_ SOL_OFF #endif #else #if SOL_IS_ON(SOL_ALL_SAFETIES_ON_I_) #define SOL_PRINT_ERRORS_I_ SOL_ON #elif SOL_IS_ON(SOL_DEBUG_BUILD_I_) #define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON #else #define SOL_PRINT_ERRORS_I_ SOL_OFF #endif #endif #if defined(SOL_DEFAULT_PASS_ON_ERROR) #if (SOL_DEFAULT_PASS_ON_ERROR != 0) #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON #else #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF #endif #else #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_OFF #endif #if defined(SOL_USING_CXX_LUA) #if (SOL_USING_CXX_LUA != 0) #define SOL_USE_CXX_LUA_I_ SOL_ON #else #define SOL_USE_CXX_LUA_I_ SOL_OFF #endif #elif defined(SOL_USE_CXX_LUA) #if (SOL_USE_CXX_LUA != 0) #define SOL_USE_CXX_LUA_I_ SOL_ON #else #define SOL_USE_CXX_LUA_I_ SOL_OFF #endif #else #define SOL_USE_CXX_LUA_I_ SOL_OFF #endif #if defined(SOL_USING_CXX_LUAJIT) #if (SOL_USING_CXX_LUA != 0) #define SOL_USE_CXX_LUAJIT_I_ SOL_ON #else #define SOL_USE_CXX_LUAJIT_I_ SOL_OFF #endif #elif defined(SOL_USE_CXX_LUAJIT) #if (SOL_USE_CXX_LUA != 0) #define SOL_USE_CXX_LUAJIT_I_ SOL_ON #else #define SOL_USE_CXX_LUAJIT_I_ SOL_OFF #endif #else #define SOL_USE_CXX_LUAJIT_I_ SOL_OFF #endif #if defined(SOL_NO_LUA_HPP) #if (SOL_NO_LUA_HPP != 0) #define SOL_USE_LUA_HPP_I_ SOL_OFF #else #define SOL_USE_LUA_HPP_I_ SOL_ON #endif #elif defined(SOL_USING_CXX_LUA) #define SOL_USE_LUA_HPP_I_ SOL_OFF #elif defined(__has_include) #if __has_include() #define SOL_USE_LUA_HPP_I_ SOL_ON #else #define SOL_USE_LUA_HPP_I_ SOL_OFF #endif #else #define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON #endif #if defined(SOL_CONTAINERS_START) #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START #elif defined(SOL_CONTAINERS_START_INDEX) #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX #elif defined(SOL_CONTAINER_START_INDEX) #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX #else #define SOL_CONTAINER_START_INDEX_I_ 1 #endif #if defined (SOL_NO_MEMORY_ALIGNMENT) #if (SOL_NO_MEMORY_ALIGNMENT != 0) #define SOL_ALIGN_MEMORY_I_ SOL_OFF #else #define SOL_ALIGN_MEMORY_I_ SOL_ON #endif #else #define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON #endif #if defined(SOL_USE_BOOST) #if (SOL_USE_BOOST != 0) #define SOL_USE_BOOST_I_ SOL_ON #else #define SOL_USE_BOOST_I_ SOL_OFF #endif #else #define SOL_USE_BOOST_I_ SOL_OFF #endif #if defined(SOL_USE_UNSAFE_BASE_LOOKUP) #if (SOL_USE_UNSAFE_BASE_LOOKUP != 0) #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON #else #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF #endif #else #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF #endif #if defined(SOL_INSIDE_UNREAL) #if (SOL_INSIDE_UNREAL != 0) #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON #else #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF #endif #else #if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER) #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON #else #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF #endif #endif #if defined(SOL_NO_COMPAT) #if (SOL_NO_COMPAT != 0) #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF #else #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON #endif #else #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON #endif #if defined(SOL_GET_FUNCTION_POINTER_UNSAFE) #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0) #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON #else #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF #endif #else #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF #endif #if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6) // MinGW is off its rocker in some places... #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON #else #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF #endif // end of sol/version.hpp #if SOL_IS_ON(SOL_INSIDE_UNREAL_ENGINE_I_) #ifdef check #pragma push_macro("check") #undef check #endif #endif // Unreal Engine 4 Bullshit #if SOL_IS_ON(SOL_COMPILER_GCC_I_) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" #pragma GCC diagnostic ignored "-Wconversion" #if __GNUC__ > 6 #pragma GCC diagnostic ignored "-Wnoexcept-type" #endif #elif SOL_IS_ON(SOL_COMPILER_CLANG_I_) #elif SOL_IS_ON(SOL_COMPILER_VCXX_I_) #pragma warning(push) #pragma warning(disable : 4505) // unreferenced local function has been removed GEE THANKS #endif // clang++ vs. g++ vs. VC++ // beginning of sol/forward.hpp #ifndef SOL_FORWARD_HPP #define SOL_FORWARD_HPP #include #include #include #if SOL_IS_ON(SOL_USE_CXX_LUA_I_) || SOL_IS_ON(SOL_USE_CXX_LUAJIT_I_) struct lua_State; #else extern "C" { struct lua_State; } #endif // C++ Mangling for Lua vs. Not namespace sol { enum class type; class stateless_reference; template class basic_reference; using reference = basic_reference; using main_reference = basic_reference; class stateless_stack_reference; class stack_reference; template class basic_bytecode; struct lua_value; struct proxy_base_tag; template struct proxy_base; template struct table_proxy; template class basic_table_core; template using table_core = basic_table_core; template using main_table_core = basic_table_core; template using stack_table_core = basic_table_core; template using basic_table = basic_table_core; using table = table_core; using global_table = table_core; using main_table = main_table_core; using main_global_table = main_table_core; using stack_table = stack_table_core; using stack_global_table = stack_table_core; template struct basic_lua_table; using lua_table = basic_lua_table; using stack_lua_table = basic_lua_table; template class basic_usertype; template using usertype = basic_usertype; template using stack_usertype = basic_usertype; template class basic_metatable; using metatable = basic_metatable; using stack_metatable = basic_metatable; template struct basic_environment; using environment = basic_environment; using main_environment = basic_environment; using stack_environment = basic_environment; template class basic_function; template class basic_protected_function; using unsafe_function = basic_function; using safe_function = basic_protected_function; using main_unsafe_function = basic_function; using main_safe_function = basic_protected_function; using stack_unsafe_function = basic_function; using stack_safe_function = basic_protected_function; using stack_aligned_unsafe_function = basic_function; using stack_aligned_safe_function = basic_protected_function; using protected_function = safe_function; using main_protected_function = main_safe_function; using stack_protected_function = stack_safe_function; using stack_aligned_protected_function = stack_aligned_safe_function; #if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS_I_) using function = protected_function; using main_function = main_protected_function; using stack_function = stack_protected_function; using stack_aligned_function = stack_aligned_safe_function; #else using function = unsafe_function; using main_function = main_unsafe_function; using stack_function = stack_unsafe_function; using stack_aligned_function = stack_aligned_unsafe_function; #endif using stack_aligned_stack_handler_function = basic_protected_function; struct unsafe_function_result; struct protected_function_result; using safe_function_result = protected_function_result; #if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS_I_) using function_result = safe_function_result; #else using function_result = unsafe_function_result; #endif template class basic_object_base; template class basic_object; template class basic_userdata; template class basic_lightuserdata; template class basic_coroutine; template class basic_thread; using object = basic_object; using userdata = basic_userdata; using lightuserdata = basic_lightuserdata; using thread = basic_thread; using coroutine = basic_coroutine; using main_object = basic_object; using main_userdata = basic_userdata; using main_lightuserdata = basic_lightuserdata; using main_coroutine = basic_coroutine; using stack_object = basic_object; using stack_userdata = basic_userdata; using stack_lightuserdata = basic_lightuserdata; using stack_thread = basic_thread; using stack_coroutine = basic_coroutine; struct stack_proxy_base; struct stack_proxy; struct variadic_args; struct variadic_results; struct stack_count; struct this_state; struct this_main_state; struct this_environment; class state_view; class state; template struct as_table_t; template struct as_container_t; template struct nested; template struct light; template struct user; template struct as_args_t; template struct protect_t; template struct policy_wrapper; template struct usertype_traits; template struct unique_usertype_traits; template struct types { typedef std::make_index_sequence indices; static constexpr std::size_t size() { return sizeof...(Args); } }; template struct derive : std::false_type { typedef types<> type; }; template struct base : std::false_type { typedef types<> type; }; template struct weak_derive { static bool value; }; template bool weak_derive::value = false; namespace stack { struct record; } #if SOL_IS_OFF(SOL_USE_BOOST_I_) template class optional; template class optional; #endif using check_handler_type = int(lua_State*, int, type, type, const char*); } // namespace sol #define SOL_BASE_CLASSES(T, ...) \ namespace sol { \ template <> \ struct base : std::true_type { \ typedef ::sol::types<__VA_ARGS__> type; \ }; \ } \ void a_sol3_detail_function_decl_please_no_collide() #define SOL_DERIVED_CLASSES(T, ...) \ namespace sol { \ template <> \ struct derive : std::true_type { \ typedef ::sol::types<__VA_ARGS__> type; \ }; \ } \ void a_sol3_detail_function_decl_please_no_collide() #endif // SOL_FORWARD_HPP // end of sol/forward.hpp // beginning of sol/forward_detail.hpp #ifndef SOL_FORWARD_DETAIL_HPP #define SOL_FORWARD_DETAIL_HPP // beginning of sol/traits.hpp // beginning of sol/tuple.hpp // beginning of sol/base_traits.hpp #include namespace sol { namespace detail { struct unchecked_t {}; const unchecked_t unchecked = unchecked_t{}; } // namespace detail namespace meta { using sfinae_yes_t = std::true_type; using sfinae_no_t = std::false_type; template using void_t = void; template using unqualified = std::remove_cv>; template using unqualified_t = typename unqualified::type; namespace meta_detail { template struct unqualified_non_alias : unqualified {}; template