[
  {
    "path": ".appveyor.yml",
    "content": "version: '#{build}'\nskip_tags: true\n\nbuild: off\ndeploy: off\n\nenvironment:\n  matrix:\n\n    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015\n      PATH: C:\\mingw-w64\\x86_64-6.3.0-posix-seh-rt_v5-rev1\\mingw64\\bin;%PATH%\n      CC: gcc\n\n    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015\n      PATH: C:\\mingw-w64\\i686-6.3.0-posix-dwarf-rt_v5-rev1\\mingw32\\bin;%PATH%\n      CC: gcc\n\n    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017\n\ntest_script:\n  - if \"%CC%\" == \"\" call build.cmd\n  - if \"%CC%\" == \"gcc\" mingw32-make\n"
  },
  {
    "path": ".gitignore",
    "content": "pkg2zip\npkg2zip.exe\n*.d\n*.o\n*.obj\n*.pdb\n*.zip\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: c\n\nmatrix:\n  include:\n\n    - os: linux\n      dist: trusty\n      sudo: false\n      compiler: gcc\n\n    - os: linux\n      dist: trusty\n      sudo: false\n      compiler: clang\n\n    - os: osx\n      osx_image: xcode9.1\n      compiler: clang\n\nscript:\n  - make\n"
  },
  {
    "path": "LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org>\n"
  },
  {
    "path": "README.md",
    "content": "# pkg2zip\n\n[![Travis CI Build Status][img_travis]][travis] [![AppVeyor Build Status][img_appveyor]][appveyor] [![Downloads][img_downloads]][downloads] [![Release][img_latest]][latest] [![License][img_license]][license]\n\nUtility that decrypts PlayStation Vita pkg file and creates zip package. Supported pkg files - main application, DLC, patch, theme and PSM files. Also supports PSX and PSP pkg files for use with [Adrenaline][].\n\nOptionally writes [NoNpDrm][] or [NoPsmDrm][] fake license file from zRIF string. You must provide license key.\n\n# Requirements\n\n* [Henkaku][] / [Enso][]\n* [Enso][] for Vita Theme installation via bgdl.\n* [NoNpDrm][]\n* [NoPsmDrm][] for PSM titles\n* [VitaShell][] **v1.76** or newer required for DLC installation\n* [Adrenaline][] for PSX or PSP titles\n* [npdrm_free][] for PSP titles in eboot format.\n\n# Features\n\n* **portable**, written in cross-platform C code, runs on Windows, GNU/Linux, macOS (system dependent functionality is isolated in sys.c file).\n* **small**, has no external library dependencies and uses very minimal dynamic memory allocations.\n* **fast**, uses AESNI hardware accelerated AES decryption if supported by CPU (requires [AESNI][] and [SSSE3][] instructions).\n* **simple**, creates zip package with same folder structure that Vita expects (just drag & drop all file from zip archive to ux0:). Zip file is created directly from pkg without any intermediate temporary files.\n* **Vita DLC**, **Vita PATCH** and **PSM** pkg unpacking.\n* **PSX**, **PSP**, **PSP Updates**, **PSP DLC**, and **PSP THEME** pkg unpacking.\n\nLimitations:\n\n* no actual title name is extracted for PSM pkg files.\n\n# Usage\n\nIf you have zRIF fake license, then execute:\n\n    pkg2zip package.pkg zRIF_STRING\n\nThis will create `title [id] [region].zip` file. Title, ID and region is automatically detected from pkg file. It will include work.bin file.\n\nIf you don't have zRIF fake license, but just want to unpack files, then omit last argument:\n\n    pkg2zip package.pkg\n\nResulting zip file will not include work.bin. This is useful for patch pkg files.\n\nTo get output file name of the zip, use `-l` (must come before pkg file and cannot be used with `-x`):\n\n    pkg2zip -l package.pkg\n\nTo avoid zipping process and create individual files, use `-x` argument (must come before pkg file):\n\n    pkg2zip -x package.pkg [zRIF_STRING]\n\nTo disable bgdl output for VITA Theme extraction, use the '-b' argument.\n\n    pkg2zip -b -x package.pkg zRIF_STRING\n\nPSX or PSP pkg files do not require zRIF argument. It will be ignored.\n\nFor PSP files pkg2zip by default will create a .ISO file. To create a compressed .CSO file pass -cN argument where N is compression factor. For example, for fastest compression use:\n\n    pkg2zip -c1 package.pkg\n\nTo create smaller cso file (more compression will require more time) use -c9, or anything inbetween:\n\n    pkg2zip -c9 package.pkg\n\nYou can combine -cN argument together with -x:\n\n    pkg2zip -x -c9 package.pkg\n\nTo extract PSP files in their original EBOOT.PBP format use the '-p' argument: \n\n    pkg2zip -p package.pkg\nNote: On PSP hardware titles with DLC should be kept in ISO/CSO format due to limitations on the CFW NoDRM Engine.\n\n# Generating zRIF string\n\nIf you have working NoNpDrm license file (work.bin or 6488b73b912a753a492e2714e9b38bc7.rif) you can create zRIF string with `rif2zrif.py` python script:\n\n    $ python rif2zrif.py path/to/work.bin\n\nIt will print zRIF string to stdout.\n\nTo generate work.bin from zRIF string use `zrif2rif.py` script:\n\n    $ python zrif2rif.py zRIF work.bin\n\nLast argument is optional, it specifies where to save file and defaults to work.bin name.\n\n# Download\n\nGet latest Windows binaries [here][downloads].\n\nArchLinux users can build binary with [pkg2zip][AUR] package in AUR repository. For example, with pacaur:\n\n    $ pacaur -S pkg2zip\n\nopenSUSE users can download the package from the [Packman](http://packman.links2linux.de/package/pkg2zip) repository.\nIf this repository is enabled, just install pkg2zip with zypper.\n\n    # zypper install pkg2zip\n\n# Building\n\nExecute `make` if you are on GNU/Linux or macOS (gcc comiler required)\nTo install the resulting file use `make install` as root\n\nOn Windows you can build either with MinGW (get [MinGW-w64][]) or [Visual Studio 2017 Community Edition][vs2017ce].\n* for MinGW make sure you have make installed, and then execute `mingw32-make`\n* for Visual Studio run `build.cmd`\n\n# Alternatives\n\n* https://github.com/RikuKH3/unpkg_vita\n* https://github.com/St4rk/PkgDecrypt\n* https://github.com/weaknespase/PkgDecrypt\n\n# License\n\nThis is free and unencumbered software released into the public domain.\n\nAnyone 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.\n\n[travis]: https://travis-ci.org/mmozeiko/pkg2zip/\n[appveyor]: https://ci.appveyor.com/project/mmozeiko/pkg2zip/\n[downloads]: https://github.com/lusid1/pkg2zip/releases\n[latest]: https://github.com/lusid1/pkg2zip/releases/latest\n[license]: https://github.com/lusid1/pkg2zip/blob/master/LICENSE\n[img_travis]: https://api.travis-ci.org/mmozeiko/pkg2zip.svg?branch=master\n[img_appveyor]: https://ci.appveyor.com/api/projects/status/xmkl6509ahlp9b7k/branch/master?svg=true\n[img_downloads]: https://img.shields.io/github/downloads/lusid1/pkg2zip/total.svg?maxAge=3600\n[img_latest]: https://img.shields.io/github/release/lusid1/pkg2zip.svg?maxAge=3600\n[img_license]: https://img.shields.io/github/license/mmozeiko/pkg2zip.svg?maxAge=2592000\n[Adrenaline]: https://github.com/TheOfficialFloW/Adrenaline\n[NoNpDrm]: https://github.com/TheOfficialFloW/NoNpDrm\n[NoPsmDrm]: https://github.com/frangarcj/NoPsmDrm\n[Henkaku]: https://henkaku.xyz/\n[Enso]: https://enso.henkaku.xyz/\n[VitaShell]: https://github.com/TheOfficialFloW/VitaShell\n[AESNI]: https://en.wikipedia.org/wiki/AES_instruction_set\n[SSSE3]: https://en.wikipedia.org/wiki/SSSE3\n[AUR]: https://aur.archlinux.org/packages/pkg2zip/\n[MinGW-w64]: http://www.msys2.org/\n[vs2017ce]: https://www.visualstudio.com/vs/community/\n[npdrm_free]: https://github.com/qwikrazor87/npdrm_free\n"
  },
  {
    "path": "build.cmd",
    "content": "@echo off\n\ncall \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Common7\\Tools\\VsDevCmd.bat\" -arch=amd64 -host_arch=amd64\ncd /d \"%~dp0\"\n\nset CL=/nologo /errorReport:none /Gm- /GF /GS- /MP /MT /W4 /WX /wd4324 /D_CRT_SECURE_NO_DEPRECATE\nset LINK=/errorReport:none /INCREMENTAL:NO\n\nset CL=%CL% /Ox\nrem set CL=%CL% /Od /Zi\nrem set LINK=%LINK% /DEBUG\n\ncl.exe pkg2zip*.c miniz_tdef.c puff.c /Fepkg2zip.exe\n"
  },
  {
    "path": "build32.cmd",
    "content": "@echo off\n\ncall \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Common7\\Tools\\VsDevCmd.bat\" -arch=x86 -host_arch=x86\ncd /d \"%~dp0\"\n\nset CL=/nologo /errorReport:none /Gm- /GF /GS- /MP /MT /W4 /WX /wd4324 /D_CRT_SECURE_NO_DEPRECATE\nset LINK=/errorReport:none /INCREMENTAL:NO\n\nset CL=%CL% /Ox\nrem set CL=%CL% /Od /Zi\nrem set LINK=%LINK% /DEBUG\n\ncl.exe pkg2zip*.c miniz_tdef.c puff.c /Fepkg2zip.exe\n"
  },
  {
    "path": "makefile",
    "content": "ifeq ($(OS),Windows_NT)\n  RM := del /q\n  EXE := .exe\nelse\n  EXE :=\nendif\n\nDESTDIR =\nPREFIX = /usr/local\nBINDIR = $(PREFIX)/bin\n\nBIN = pkg2zip${EXE}\nSRC = ${wildcard pkg2zip*.c} miniz_tdef.c puff.c\nOBJ = ${SRC:.c=.o}\nDEP = ${SRC:.c=.d}\n\nCFLAGS = -std=c99 -pipe -fvisibility=hidden -Wall -Wextra -Werror -DNDEBUG -D_GNU_SOURCE -O2\n${BIN}: LDFLAGS += -s\ndebug: CFLAGS += -g\ndebug: LDFLAGS += -g\n\n.PHONY: all clean\n\nall: ${BIN}\n\ninstall:\n\tinstall -D -m 0755 ${BIN} $(DESTDIR)$(BINDIR)/${BIN}\n\tinstall -D -m 0755 rif2zrif.py $(DESTDIR)$(BINDIR)/rif2zrif\n\tinstall -D -m 0755 zrif2rif.py $(DESTDIR)$(BINDIR)/zrif2rif\n\nuninstall:\n\trm -f $(DESTDIR)$(BINDIR)/${BIN}\n\trm -f $(DESTDIR)$(BINDIR)/rif2zrif\n\trm -f $(DESTDIR)$(BINDIR)/zrif2rif\n\nclean:\n\t@${RM} ${BIN} ${OBJ} ${DEP}\n\n${BIN}: ${OBJ}\n\t@echo [L] $@\n\t@${CC} ${LDFLAGS} -o $@ $^\n\ndebug: ${OBJ}\n\t@echo [L] ${BIN}\n\t@${CC} ${LDFLAGS} -o ${BIN} $^\n\n%aes_x86.o: %aes_x86.c\n\t@echo [C] $<\n\t@${CC} ${CFLAGS} -maes -mssse3 -MMD -c -o $@ $<\n\n%crc32_x86.o: %crc32_x86.c\n\t@echo [C] $<\n\t@${CC} ${CFLAGS} -mpclmul -msse4 -MMD -c -o $@ $<\n\n%.o: %.c\n\t@echo [C] $<\n\t@${CC} ${CFLAGS} -MMD -c -o $@ $<\n\n-include ${DEP}\n"
  },
  {
    "path": "miniz_tdef.c",
    "content": "#include \"miniz_tdef.h\"\n\n#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)\n/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */\n#define MINIZ_X86_OR_X64_CPU 1\n#else\n#define MINIZ_X86_OR_X64_CPU 0\n#endif\n\n#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU\n/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */\n#define MINIZ_LITTLE_ENDIAN 1\n#else\n#define MINIZ_LITTLE_ENDIAN 0\n#endif\n\n#if MINIZ_X86_OR_X64_CPU\n/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */\n#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1\n#else\n#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0\n#endif\n\n#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)\n/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */\n#define MINIZ_HAS_64BIT_REGISTERS 1\n#else\n#define MINIZ_HAS_64BIT_REGISTERS 0\n#endif\n\n// ------------------- Low-level Compression (independent from all decompression API's)\n\n// Purposely making these tables static for faster init and thread safety.\nstatic const mz_uint16 s_tdefl_len_sym[256] =\n    {\n        257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272,\n        273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276,\n        277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,\n        279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,\n        281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281,\n        282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282,\n        283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,\n        284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285\n    };\n\nstatic const mz_uint8 s_tdefl_len_extra[256] =\n    {\n        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 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, 3, 3, 3, 3, 3, 3,\n        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, 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, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n        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,\n        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\n    };\n\nstatic const mz_uint8 s_tdefl_small_dist_sym[512] =\n    {\n        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,\n        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,\n        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,\n        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,\n        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,\n        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, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,\n        16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,\n        16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,\n        16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,\n        17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,\n        17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,\n        17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17\n    };\n\nstatic const mz_uint8 s_tdefl_small_dist_extra[512] =\n    {\n        0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 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, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,\n        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, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,\n        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,\n        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 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,\n        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, 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, 7, 7, 7, 7, 7, 7, 7, 7,\n        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, 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, 7, 7, 7, 7, 7, 7, 7, 7,\n        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, 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, 7, 7, 7, 7, 7, 7, 7, 7,\n        7, 7, 7, 7, 7, 7, 7, 7\n    };\n\nstatic const mz_uint8 s_tdefl_large_dist_sym[128] =\n    {\n        0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,\n        26, 26, 26, 26, 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,\n        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\n    };\n\nstatic const mz_uint8 s_tdefl_large_dist_extra[128] =\n    {\n        0, 0, 8, 8, 9, 9, 9, 9, 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,\n        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,\n        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\n    };\n\n// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values.\ntypedef struct\n{\n    mz_uint16 m_key, m_sym_index;\n} tdefl_sym_freq;\nstatic tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1)\n{\n    mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2];\n    tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1;\n    MZ_CLEAR_OBJ(hist);\n    for (i = 0; i < num_syms; i++)\n    {\n        mz_uint freq = pSyms0[i].m_key;\n        hist[freq & 0xFF]++;\n        hist[256 + ((freq >> 8) & 0xFF)]++;\n    }\n    while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256]))\n        total_passes--;\n    for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)\n    {\n        const mz_uint32 *pHist = &hist[pass << 8];\n        mz_uint offsets[256], cur_ofs = 0;\n        for (i = 0; i < 256; i++)\n        {\n            offsets[i] = cur_ofs;\n            cur_ofs += pHist[i];\n        }\n        for (i = 0; i < num_syms; i++)\n            pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];\n        {\n            tdefl_sym_freq *t = pCur_syms;\n            pCur_syms = pNew_syms;\n            pNew_syms = t;\n        }\n    }\n    return pCur_syms;\n}\n\n// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996.\nstatic void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)\n{\n    int root, leaf, next, avbl, used, dpth;\n    if (n == 0)\n        return;\n    else if (n == 1)\n    {\n        A[0].m_key = 1;\n        return;\n    }\n    A[0].m_key += A[1].m_key;\n    root = 0;\n    leaf = 2;\n    for (next = 1; next < n - 1; next++)\n    {\n        if (leaf >= n || A[root].m_key < A[leaf].m_key)\n        {\n            A[next].m_key = A[root].m_key;\n            A[root++].m_key = (mz_uint16)next;\n        }\n        else\n            A[next].m_key = A[leaf++].m_key;\n        if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key))\n        {\n            A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key);\n            A[root++].m_key = (mz_uint16)next;\n        }\n        else\n            A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);\n    }\n    A[n - 2].m_key = 0;\n    for (next = n - 3; next >= 0; next--)\n        A[next].m_key = A[A[next].m_key].m_key + 1;\n    avbl = 1;\n    used = dpth = 0;\n    root = n - 2;\n    next = n - 1;\n    while (avbl > 0)\n    {\n        while (root >= 0 && (int)A[root].m_key == dpth)\n        {\n            used++;\n            root--;\n        }\n        while (avbl > used)\n        {\n            A[next--].m_key = (mz_uint16)(dpth);\n            avbl--;\n        }\n        avbl = 2 * used;\n        dpth++;\n        used = 0;\n    }\n}\n\n// Limits canonical Huffman code table's max code size.\nenum\n{\n    TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32\n};\nstatic void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)\n{\n    int i;\n    mz_uint32 total = 0;\n    if (code_list_len <= 1)\n        return;\n    for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++)\n        pNum_codes[max_code_size] += pNum_codes[i];\n    for (i = max_code_size; i > 0; i--)\n        total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));\n    while (total != (1UL << max_code_size))\n    {\n        pNum_codes[max_code_size]--;\n        for (i = max_code_size - 1; i > 0; i--)\n            if (pNum_codes[i])\n            {\n                pNum_codes[i]--;\n                pNum_codes[i + 1] += 2;\n                break;\n            }\n        total--;\n    }\n}\n\nstatic void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)\n{\n    int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE];\n    mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1];\n    MZ_CLEAR_OBJ(num_codes);\n    if (static_table)\n    {\n        for (i = 0; i < table_len; i++)\n            num_codes[d->m_huff_code_sizes[table_num][i]]++;\n    }\n    else\n    {\n        tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;\n        int num_used_syms = 0;\n        const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];\n        for (i = 0; i < table_len; i++)\n            if (pSym_count[i])\n            {\n                syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i];\n                syms0[num_used_syms++].m_sym_index = (mz_uint16)i;\n            }\n\n        pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1);\n        tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);\n\n        for (i = 0; i < num_used_syms; i++)\n            num_codes[pSyms[i].m_key]++;\n\n        tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);\n\n        MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]);\n        MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);\n        for (i = 1, j = num_used_syms; i <= code_size_limit; i++)\n            for (l = num_codes[i]; l > 0; l--)\n                d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);\n    }\n\n    next_code[1] = 0;\n    for (j = 0, i = 2; i <= code_size_limit; i++)\n        next_code[i] = j = ((j + num_codes[i - 1]) << 1);\n\n    for (i = 0; i < table_len; i++)\n    {\n        mz_uint rev_code = 0, code, code_size;\n        if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0)\n            continue;\n        code = next_code[code_size]++;\n        for (l = code_size; l > 0; l--, code >>= 1)\n            rev_code = (rev_code << 1) | (code & 1);\n        d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;\n    }\n}\n\n#define TDEFL_PUT_BITS(b, l)                                       \\\n    do                                                             \\\n    {                                                              \\\n        mz_uint bits = b;                                          \\\n        mz_uint len = l;                                           \\\n        MZ_ASSERT(bits <= ((1U << len) - 1U));                     \\\n        d->m_bit_buffer |= (bits << d->m_bits_in);                 \\\n        d->m_bits_in += len;                                       \\\n        while (d->m_bits_in >= 8)                                  \\\n        {                                                          \\\n            if (d->m_pOutput_buf < d->m_pOutput_buf_end)           \\\n                *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \\\n            d->m_bit_buffer >>= 8;                                 \\\n            d->m_bits_in -= 8;                                     \\\n        }                                                          \\\n    }                                                              \\\n    MZ_MACRO_END\n\n#define TDEFL_RLE_PREV_CODE_SIZE()                                                                                       \\\n    {                                                                                                                    \\\n        if (rle_repeat_count)                                                                                            \\\n        {                                                                                                                \\\n            if (rle_repeat_count < 3)                                                                                    \\\n            {                                                                                                            \\\n                d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \\\n                while (rle_repeat_count--)                                                                               \\\n                    packed_code_sizes[num_packed_code_sizes++] = prev_code_size;                                         \\\n            }                                                                                                            \\\n            else                                                                                                         \\\n            {                                                                                                            \\\n                d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1);                                        \\\n                packed_code_sizes[num_packed_code_sizes++] = 16;                                                         \\\n                packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3);                           \\\n            }                                                                                                            \\\n            rle_repeat_count = 0;                                                                                        \\\n        }                                                                                                                \\\n    }\n\n#define TDEFL_RLE_ZERO_CODE_SIZE()                                                         \\\n    {                                                                                      \\\n        if (rle_z_count)                                                                   \\\n        {                                                                                  \\\n            if (rle_z_count < 3)                                                           \\\n            {                                                                              \\\n                d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count);  \\\n                while (rle_z_count--)                                                      \\\n                    packed_code_sizes[num_packed_code_sizes++] = 0;                        \\\n            }                                                                              \\\n            else if (rle_z_count <= 10)                                                    \\\n            {                                                                              \\\n                d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1);          \\\n                packed_code_sizes[num_packed_code_sizes++] = 17;                           \\\n                packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3);  \\\n            }                                                                              \\\n            else                                                                           \\\n            {                                                                              \\\n                d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1);          \\\n                packed_code_sizes[num_packed_code_sizes++] = 18;                           \\\n                packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \\\n            }                                                                              \\\n            rle_z_count = 0;                                                               \\\n        }                                                                                  \\\n    }\n\nstatic mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };\n\nstatic void tdefl_start_dynamic_block(tdefl_compressor *d)\n{\n    int num_lit_codes, num_dist_codes, num_bit_lengths;\n    mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;\n    mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;\n\n    d->m_huff_count[0][256] = 1;\n\n    tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);\n    tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);\n\n    for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--)\n        if (d->m_huff_code_sizes[0][num_lit_codes - 1])\n            break;\n    for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--)\n        if (d->m_huff_code_sizes[1][num_dist_codes - 1])\n            break;\n\n    memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);\n    memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);\n    total_code_sizes_to_pack = num_lit_codes + num_dist_codes;\n    num_packed_code_sizes = 0;\n    rle_z_count = 0;\n    rle_repeat_count = 0;\n\n    memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);\n    for (i = 0; i < total_code_sizes_to_pack; i++)\n    {\n        mz_uint8 code_size = code_sizes_to_pack[i];\n        if (!code_size)\n        {\n            TDEFL_RLE_PREV_CODE_SIZE();\n            if (++rle_z_count == 138)\n            {\n                TDEFL_RLE_ZERO_CODE_SIZE();\n            }\n        }\n        else\n        {\n            TDEFL_RLE_ZERO_CODE_SIZE();\n            if (code_size != prev_code_size)\n            {\n                TDEFL_RLE_PREV_CODE_SIZE();\n                d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1);\n                packed_code_sizes[num_packed_code_sizes++] = code_size;\n            }\n            else if (++rle_repeat_count == 6)\n            {\n                TDEFL_RLE_PREV_CODE_SIZE();\n            }\n        }\n        prev_code_size = code_size;\n    }\n    if (rle_repeat_count)\n    {\n        TDEFL_RLE_PREV_CODE_SIZE();\n    }\n    else\n    {\n        TDEFL_RLE_ZERO_CODE_SIZE();\n    }\n\n    tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);\n\n    TDEFL_PUT_BITS(2, 2);\n\n    TDEFL_PUT_BITS(num_lit_codes - 257, 5);\n    TDEFL_PUT_BITS(num_dist_codes - 1, 5);\n\n    for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--)\n        if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]])\n            break;\n    num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1));\n    TDEFL_PUT_BITS(num_bit_lengths - 4, 4);\n    for (i = 0; (int)i < num_bit_lengths; i++)\n        TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);\n\n    for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;)\n    {\n        mz_uint code = packed_code_sizes[packed_code_sizes_index++];\n        MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);\n        TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);\n        if (code >= 16)\n            TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], \"\\02\\03\\07\"[code - 16]);\n    }\n}\n\nstatic void tdefl_start_static_block(tdefl_compressor *d)\n{\n    mz_uint i;\n    mz_uint8 *p = &d->m_huff_code_sizes[0][0];\n\n    for (i = 0; i <= 143; ++i)\n        *p++ = 8;\n    for (; i <= 255; ++i)\n        *p++ = 9;\n    for (; i <= 279; ++i)\n        *p++ = 7;\n    for (; i <= 287; ++i)\n        *p++ = 8;\n\n    memset(d->m_huff_code_sizes[1], 5, 32);\n\n    tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);\n    tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);\n\n    TDEFL_PUT_BITS(1, 2);\n}\n\nstatic const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS\nstatic mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)\n{\n    mz_uint flags;\n    mz_uint8 *pLZ_codes;\n    mz_uint8 *pOutput_buf = d->m_pOutput_buf;\n    mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;\n    mz_uint64 bit_buffer = d->m_bit_buffer;\n    mz_uint bits_in = d->m_bits_in;\n\n#define TDEFL_PUT_BITS_FAST(b, l)                    \\\n    {                                                \\\n        bit_buffer |= (((mz_uint64)(b)) << bits_in); \\\n        bits_in += (l);                              \\\n    }\n\n    flags = 1;\n    for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)\n    {\n        if (flags == 1)\n            flags = *pLZ_codes++ | 0x100;\n\n        if (flags & 1)\n        {\n            mz_uint s0, s1, n0, n1, sym, num_extra_bits;\n            mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1);\n            pLZ_codes += 3;\n\n            MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);\n            TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);\n            TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);\n\n            // This sequence coaxes MSVC into using cmov's vs. jmp's.\n            s0 = s_tdefl_small_dist_sym[match_dist & 511];\n            n0 = s_tdefl_small_dist_extra[match_dist & 511];\n            s1 = s_tdefl_large_dist_sym[match_dist >> 8];\n            n1 = s_tdefl_large_dist_extra[match_dist >> 8];\n            sym = (match_dist < 512) ? s0 : s1;\n            num_extra_bits = (match_dist < 512) ? n0 : n1;\n\n            MZ_ASSERT(d->m_huff_code_sizes[1][sym]);\n            TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);\n            TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);\n        }\n        else\n        {\n            mz_uint lit = *pLZ_codes++;\n            MZ_ASSERT(d->m_huff_code_sizes[0][lit]);\n            TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);\n\n            if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))\n            {\n                flags >>= 1;\n                lit = *pLZ_codes++;\n                MZ_ASSERT(d->m_huff_code_sizes[0][lit]);\n                TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);\n\n                if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))\n                {\n                    flags >>= 1;\n                    lit = *pLZ_codes++;\n                    MZ_ASSERT(d->m_huff_code_sizes[0][lit]);\n                    TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);\n                }\n            }\n        }\n\n        if (pOutput_buf >= d->m_pOutput_buf_end)\n            return MZ_FALSE;\n\n        *(mz_uint64 *)pOutput_buf = bit_buffer;\n        pOutput_buf += (bits_in >> 3);\n        bit_buffer >>= (bits_in & ~7);\n        bits_in &= 7;\n    }\n\n#undef TDEFL_PUT_BITS_FAST\n\n    d->m_pOutput_buf = pOutput_buf;\n    d->m_bits_in = 0;\n    d->m_bit_buffer = 0;\n\n    while (bits_in)\n    {\n        mz_uint32 n = MZ_MIN(bits_in, 16);\n        TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);\n        bit_buffer >>= n;\n        bits_in -= n;\n    }\n\n    TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);\n\n    return (d->m_pOutput_buf < d->m_pOutput_buf_end);\n}\n#else\nstatic mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)\n{\n    mz_uint flags;\n    mz_uint8 *pLZ_codes;\n\n    flags = 1;\n    for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)\n    {\n        if (flags == 1)\n            flags = *pLZ_codes++ | 0x100;\n        if (flags & 1)\n        {\n            mz_uint sym, num_extra_bits;\n            mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8));\n            pLZ_codes += 3;\n\n            MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);\n            TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);\n            TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);\n\n            if (match_dist < 512)\n            {\n                sym = s_tdefl_small_dist_sym[match_dist];\n                num_extra_bits = s_tdefl_small_dist_extra[match_dist];\n            }\n            else\n            {\n                sym = s_tdefl_large_dist_sym[match_dist >> 8];\n                num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];\n            }\n            MZ_ASSERT(d->m_huff_code_sizes[1][sym]);\n            TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);\n            TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);\n        }\n        else\n        {\n            mz_uint lit = *pLZ_codes++;\n            MZ_ASSERT(d->m_huff_code_sizes[0][lit]);\n            TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);\n        }\n    }\n\n    TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);\n\n    return (d->m_pOutput_buf < d->m_pOutput_buf_end);\n}\n#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS\n\nstatic mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)\n{\n    if (static_block)\n        tdefl_start_static_block(d);\n    else\n        tdefl_start_dynamic_block(d);\n    return tdefl_compress_lz_codes(d);\n}\n\nstatic int tdefl_flush_block(tdefl_compressor *d, int flush)\n{\n    mz_uint saved_bit_buf, saved_bits_in;\n    mz_uint8 *pSaved_output_buf;\n    mz_bool comp_block_succeeded = MZ_FALSE;\n    int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;\n    mz_uint8 *pOutput_buf_start = ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;\n\n    d->m_pOutput_buf = pOutput_buf_start;\n    d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;\n\n    MZ_ASSERT(!d->m_output_flush_remaining);\n    d->m_output_flush_ofs = 0;\n    d->m_output_flush_remaining = 0;\n\n    *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);\n    d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);\n\n    if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))\n    {\n        TDEFL_PUT_BITS(0x78, 8);\n        TDEFL_PUT_BITS(0x01, 8);\n    }\n\n    TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);\n\n    pSaved_output_buf = d->m_pOutput_buf;\n    saved_bit_buf = d->m_bit_buffer;\n    saved_bits_in = d->m_bits_in;\n\n    if (!use_raw_block)\n        comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));\n\n    // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead.\n    if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&\n        ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size))\n    {\n        mz_uint i;\n        d->m_pOutput_buf = pSaved_output_buf;\n        d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;\n        TDEFL_PUT_BITS(0, 2);\n        if (d->m_bits_in)\n        {\n            TDEFL_PUT_BITS(0, 8 - d->m_bits_in);\n        }\n        for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)\n        {\n            TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);\n        }\n        for (i = 0; i < d->m_total_lz_bytes; ++i)\n        {\n            TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);\n        }\n    }\n    // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes.\n    else if (!comp_block_succeeded)\n    {\n        d->m_pOutput_buf = pSaved_output_buf;\n        d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;\n        tdefl_compress_block(d, MZ_TRUE);\n    }\n\n    if (flush)\n    {\n        if (flush == TDEFL_FINISH)\n        {\n            if (d->m_bits_in)\n            {\n                TDEFL_PUT_BITS(0, 8 - d->m_bits_in);\n            }\n            if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER)\n            {\n                mz_uint i, a = d->m_adler32;\n                for (i = 0; i < 4; i++)\n                {\n                    TDEFL_PUT_BITS((a >> 24) & 0xFF, 8);\n                    a <<= 8;\n                }\n            }\n        }\n        else\n        {\n            mz_uint i, z = 0;\n            TDEFL_PUT_BITS(0, 3);\n            if (d->m_bits_in)\n            {\n                TDEFL_PUT_BITS(0, 8 - d->m_bits_in);\n            }\n            for (i = 2; i; --i, z ^= 0xFFFF)\n            {\n                TDEFL_PUT_BITS(z & 0xFFFF, 16);\n            }\n        }\n    }\n\n    MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);\n\n    memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);\n    memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);\n\n    d->m_pLZ_code_buf = d->m_lz_code_buf + 1;\n    d->m_pLZ_flags = d->m_lz_code_buf;\n    d->m_num_flags_left = 8;\n    d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes;\n    d->m_total_lz_bytes = 0;\n    d->m_block_index++;\n\n    if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)\n    {\n        if (pOutput_buf_start == d->m_output_buf)\n        {\n            int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));\n            memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);\n            d->m_out_buf_ofs += bytes_to_copy;\n            if ((n -= bytes_to_copy) != 0)\n            {\n                d->m_output_flush_ofs = bytes_to_copy;\n                d->m_output_flush_remaining = n;\n            }\n        }\n        else\n        {\n            d->m_out_buf_ofs += n;\n        }\n    }\n\n    return d->m_output_flush_remaining;\n}\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES\nstatic MZ_FORCEINLINE mz_uint16 TDEFL_READ_UNALIGNED_WORD(const void* p)\n{\n    return *(const mz_uint16 *)p;\n}\n\nstatic MZ_FORCEINLINE mz_uint32 TDEFL_READ_UNALIGNED_DWORD(const void* p)\n{\n    return *(const mz_uint32 *)p;\n}\n\nstatic MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)\n{\n    mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;\n    mz_uint num_probes_left = d->m_max_probes[match_len >= 32];\n    const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q;\n    mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s);\n    MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN);\n    if (max_match_len <= match_len)\n        return;\n    for (;;)\n    {\n        for (;;)\n        {\n            if (--num_probes_left == 0)\n                return;\n#define TDEFL_PROBE                                                                             \\\n    next_probe_pos = d->m_next[probe_pos];                                                      \\\n    if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \\\n        return;                                                                                 \\\n    probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK;                                       \\\n    if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01)                \\\n        break;\n            TDEFL_PROBE;\n            TDEFL_PROBE;\n            TDEFL_PROBE;\n        }\n        if (!dist)\n            break;\n        q = (const mz_uint16 *)(d->m_dict + probe_pos);\n        if (TDEFL_READ_UNALIGNED_WORD(q) != s01)\n            continue;\n        p = s;\n        probe_len = 32;\n        do\n        {\n        } while ((TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&\n                 (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0));\n        if (!probe_len)\n        {\n            *pMatch_dist = dist;\n            *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN);\n            break;\n        }\n        else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len)\n        {\n            *pMatch_dist = dist;\n            if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len)\n                break;\n            c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);\n        }\n    }\n}\n#else\nstatic MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)\n{\n    mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;\n    mz_uint num_probes_left = d->m_max_probes[match_len >= 32];\n    const mz_uint8 *s = d->m_dict + pos, *p, *q;\n    mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];\n    MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN);\n    if (max_match_len <= match_len)\n        return;\n    for (;;)\n    {\n        for (;;)\n        {\n            if (--num_probes_left == 0)\n                return;\n#define TDEFL_PROBE                                                                               \\\n    next_probe_pos = d->m_next[probe_pos];                                                        \\\n    if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist))   \\\n        return;                                                                                   \\\n    probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK;                                         \\\n    if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \\\n        break;\n            TDEFL_PROBE;\n            TDEFL_PROBE;\n            TDEFL_PROBE;\n        }\n        if (!dist)\n            break;\n        p = s;\n        q = d->m_dict + probe_pos;\n        for (probe_len = 0; probe_len < max_match_len; probe_len++)\n            if (*p++ != *q++)\n                break;\n        if (probe_len > match_len)\n        {\n            *pMatch_dist = dist;\n            if ((*pMatch_len = match_len = probe_len) == max_match_len)\n                return;\n            c0 = d->m_dict[pos + match_len];\n            c1 = d->m_dict[pos + match_len - 1];\n        }\n    }\n}\n#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN\nstatic mz_bool tdefl_compress_fast(tdefl_compressor *d)\n{\n    // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio.\n    mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;\n    mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;\n    mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;\n\n    while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))\n    {\n        const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;\n        mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;\n        mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);\n        d->m_src_buf_left -= num_bytes_to_process;\n        lookahead_size += num_bytes_to_process;\n\n        while (num_bytes_to_process)\n        {\n            mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);\n            memcpy(d->m_dict + dst_pos, d->m_pSrc, n);\n            if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))\n                memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));\n            d->m_pSrc += n;\n            dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;\n            num_bytes_to_process -= n;\n        }\n\n        dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);\n        if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE))\n            break;\n\n        while (lookahead_size >= 4)\n        {\n            mz_uint cur_match_dist, cur_match_len = 1;\n            mz_uint8 *pCur_dict = d->m_dict + cur_pos;\n            mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF;\n            mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;\n            mz_uint probe_pos = d->m_hash[hash];\n            d->m_hash[hash] = (mz_uint16)lookahead_pos;\n\n            if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_DWORD(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))\n            {\n                const mz_uint16 *p = (const mz_uint16 *)pCur_dict;\n                const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);\n                mz_uint32 probe_len = 32;\n                do\n                {\n                } while ((TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&\n                         (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0));\n                cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);\n                if (!probe_len)\n                    cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;\n\n                if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)))\n                {\n                    cur_match_len = 1;\n                    *pLZ_code_buf++ = (mz_uint8)first_trigram;\n                    *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);\n                    d->m_huff_count[0][(mz_uint8)first_trigram]++;\n                }\n                else\n                {\n                    mz_uint32 s0, s1;\n                    cur_match_len = MZ_MIN(cur_match_len, lookahead_size);\n\n                    MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));\n\n                    cur_match_dist--;\n\n                    pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);\n                    *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;\n                    pLZ_code_buf += 3;\n                    *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);\n\n                    s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];\n                    s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];\n                    d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;\n\n                    d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;\n                }\n            }\n            else\n            {\n                *pLZ_code_buf++ = (mz_uint8)first_trigram;\n                *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);\n                d->m_huff_count[0][(mz_uint8)first_trigram]++;\n            }\n\n            if (--num_flags_left == 0)\n            {\n                num_flags_left = 8;\n                pLZ_flags = pLZ_code_buf++;\n            }\n\n            total_lz_bytes += cur_match_len;\n            lookahead_pos += cur_match_len;\n            dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE);\n            cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;\n            MZ_ASSERT(lookahead_size >= cur_match_len);\n            lookahead_size -= cur_match_len;\n\n            if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])\n            {\n                int n;\n                d->m_lookahead_pos = lookahead_pos;\n                d->m_lookahead_size = lookahead_size;\n                d->m_dict_size = dict_size;\n                d->m_total_lz_bytes = total_lz_bytes;\n                d->m_pLZ_code_buf = pLZ_code_buf;\n                d->m_pLZ_flags = pLZ_flags;\n                d->m_num_flags_left = num_flags_left;\n                if ((n = tdefl_flush_block(d, 0)) != 0)\n                    return (n < 0) ? MZ_FALSE : MZ_TRUE;\n                total_lz_bytes = d->m_total_lz_bytes;\n                pLZ_code_buf = d->m_pLZ_code_buf;\n                pLZ_flags = d->m_pLZ_flags;\n                num_flags_left = d->m_num_flags_left;\n            }\n        }\n\n        while (lookahead_size)\n        {\n            mz_uint8 lit = d->m_dict[cur_pos];\n\n            total_lz_bytes++;\n            *pLZ_code_buf++ = lit;\n            *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);\n            if (--num_flags_left == 0)\n            {\n                num_flags_left = 8;\n                pLZ_flags = pLZ_code_buf++;\n            }\n\n            d->m_huff_count[0][lit]++;\n\n            lookahead_pos++;\n            dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE);\n            cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;\n            lookahead_size--;\n\n            if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])\n            {\n                int n;\n                d->m_lookahead_pos = lookahead_pos;\n                d->m_lookahead_size = lookahead_size;\n                d->m_dict_size = dict_size;\n                d->m_total_lz_bytes = total_lz_bytes;\n                d->m_pLZ_code_buf = pLZ_code_buf;\n                d->m_pLZ_flags = pLZ_flags;\n                d->m_num_flags_left = num_flags_left;\n                if ((n = tdefl_flush_block(d, 0)) != 0)\n                    return (n < 0) ? MZ_FALSE : MZ_TRUE;\n                total_lz_bytes = d->m_total_lz_bytes;\n                pLZ_code_buf = d->m_pLZ_code_buf;\n                pLZ_flags = d->m_pLZ_flags;\n                num_flags_left = d->m_num_flags_left;\n            }\n        }\n    }\n\n    d->m_lookahead_pos = lookahead_pos;\n    d->m_lookahead_size = lookahead_size;\n    d->m_dict_size = dict_size;\n    d->m_total_lz_bytes = total_lz_bytes;\n    d->m_pLZ_code_buf = pLZ_code_buf;\n    d->m_pLZ_flags = pLZ_flags;\n    d->m_num_flags_left = num_flags_left;\n    return MZ_TRUE;\n}\n#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN\n\nstatic MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)\n{\n    d->m_total_lz_bytes++;\n    *d->m_pLZ_code_buf++ = lit;\n    *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1);\n    if (--d->m_num_flags_left == 0)\n    {\n        d->m_num_flags_left = 8;\n        d->m_pLZ_flags = d->m_pLZ_code_buf++;\n    }\n    d->m_huff_count[0][lit]++;\n}\n\nstatic MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)\n{\n    mz_uint32 s0, s1;\n\n    MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));\n\n    d->m_total_lz_bytes += match_len;\n\n    d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);\n\n    match_dist -= 1;\n    d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);\n    d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8);\n    d->m_pLZ_code_buf += 3;\n\n    *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80);\n    if (--d->m_num_flags_left == 0)\n    {\n        d->m_num_flags_left = 8;\n        d->m_pLZ_flags = d->m_pLZ_code_buf++;\n    }\n\n    s0 = s_tdefl_small_dist_sym[match_dist & 511];\n    s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];\n    d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;\n\n    if (match_len >= TDEFL_MIN_MATCH_LEN)\n        d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;\n}\n\nstatic mz_bool tdefl_compress_normal(tdefl_compressor *d)\n{\n    const mz_uint8 *pSrc = d->m_pSrc;\n    size_t src_buf_left = d->m_src_buf_left;\n    tdefl_flush flush = d->m_flush;\n\n    while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))\n    {\n        mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;\n        // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN.\n        if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))\n        {\n            mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;\n            mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];\n            mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);\n            const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;\n            src_buf_left -= num_bytes_to_process;\n            d->m_lookahead_size += num_bytes_to_process;\n            while (pSrc != pSrc_end)\n            {\n                mz_uint8 c = *pSrc++;\n                d->m_dict[dst_pos] = c;\n                if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))\n                    d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;\n                hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);\n                d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash];\n                d->m_hash[hash] = (mz_uint16)(ins_pos);\n                dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;\n                ins_pos++;\n            }\n        }\n        else\n        {\n            while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))\n            {\n                mz_uint8 c = *pSrc++;\n                mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;\n                src_buf_left--;\n                d->m_dict[dst_pos] = c;\n                if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))\n                    d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;\n                if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)\n                {\n                    mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;\n                    mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);\n                    d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash];\n                    d->m_hash[hash] = (mz_uint16)(ins_pos);\n                }\n            }\n        }\n        d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);\n        if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))\n            break;\n\n        // Simple lazy/greedy parsing state machine.\n        len_to_move = 1;\n        cur_match_dist = 0;\n        cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1);\n        cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;\n        if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))\n        {\n            if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))\n            {\n                mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];\n                cur_match_len = 0;\n                while (cur_match_len < d->m_lookahead_size)\n                {\n                    if (d->m_dict[cur_pos + cur_match_len] != c)\n                        break;\n                    cur_match_len++;\n                }\n                if (cur_match_len < TDEFL_MIN_MATCH_LEN)\n                    cur_match_len = 0;\n                else\n                    cur_match_dist = 1;\n            }\n        }\n        else\n        {\n            tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);\n        }\n        if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))\n        {\n            cur_match_dist = cur_match_len = 0;\n        }\n        if (d->m_saved_match_len)\n        {\n            if (cur_match_len > d->m_saved_match_len)\n            {\n                tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);\n                if (cur_match_len >= 128)\n                {\n                    tdefl_record_match(d, cur_match_len, cur_match_dist);\n                    d->m_saved_match_len = 0;\n                    len_to_move = cur_match_len;\n                }\n                else\n                {\n                    d->m_saved_lit = d->m_dict[cur_pos];\n                    d->m_saved_match_dist = cur_match_dist;\n                    d->m_saved_match_len = cur_match_len;\n                }\n            }\n            else\n            {\n                tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);\n                len_to_move = d->m_saved_match_len - 1;\n                d->m_saved_match_len = 0;\n            }\n        }\n        else if (!cur_match_dist)\n            tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);\n        else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))\n        {\n            tdefl_record_match(d, cur_match_len, cur_match_dist);\n            len_to_move = cur_match_len;\n        }\n        else\n        {\n            d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)];\n            d->m_saved_match_dist = cur_match_dist;\n            d->m_saved_match_len = cur_match_len;\n        }\n        // Move the lookahead forward by len_to_move bytes.\n        d->m_lookahead_pos += len_to_move;\n        MZ_ASSERT(d->m_lookahead_size >= len_to_move);\n        d->m_lookahead_size -= len_to_move;\n        d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE);\n        // Check if it's time to flush the current LZ codes to the internal output buffer.\n        if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||\n            ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))))\n        {\n            int n;\n            d->m_pSrc = pSrc;\n            d->m_src_buf_left = src_buf_left;\n            if ((n = tdefl_flush_block(d, 0)) != 0)\n                return (n < 0) ? MZ_FALSE : MZ_TRUE;\n        }\n    }\n\n    d->m_pSrc = pSrc;\n    d->m_src_buf_left = src_buf_left;\n    return MZ_TRUE;\n}\n\nstatic tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)\n{\n    if (d->m_pIn_buf_size)\n    {\n        *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;\n    }\n\n    if (d->m_pOut_buf_size)\n    {\n        size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);\n        memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);\n        d->m_output_flush_ofs += (mz_uint)n;\n        d->m_output_flush_remaining -= (mz_uint)n;\n        d->m_out_buf_ofs += n;\n\n        *d->m_pOut_buf_size = d->m_out_buf_ofs;\n    }\n\n    return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;\n}\n\ntdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)\n{\n    if (!d)\n    {\n        if (pIn_buf_size)\n            *pIn_buf_size = 0;\n        if (pOut_buf_size)\n            *pOut_buf_size = 0;\n        return TDEFL_STATUS_BAD_PARAM;\n    }\n\n    d->m_pIn_buf = pIn_buf;\n    d->m_pIn_buf_size = pIn_buf_size;\n    d->m_pOut_buf = pOut_buf;\n    d->m_pOut_buf_size = pOut_buf_size;\n    d->m_pSrc = (const mz_uint8 *)(pIn_buf);\n    d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;\n    d->m_out_buf_ofs = 0;\n    d->m_flush = flush;\n\n    if (((0) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||\n        (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf))\n    {\n        if (pIn_buf_size)\n            *pIn_buf_size = 0;\n        if (pOut_buf_size)\n            *pOut_buf_size = 0;\n        return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);\n    }\n    d->m_wants_to_finish |= (flush == TDEFL_FINISH);\n\n    if ((d->m_output_flush_remaining) || (d->m_finished))\n        return (d->m_prev_return_status = tdefl_flush_output_buffer(d));\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN\n    if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&\n        ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&\n        ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))\n    {\n        if (!tdefl_compress_fast(d))\n            return d->m_prev_return_status;\n    }\n    else\n#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN\n    {\n        if (!tdefl_compress_normal(d))\n            return d->m_prev_return_status;\n    }\n\n    if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))\n        d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);\n\n    if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))\n    {\n        if (tdefl_flush_block(d, flush) < 0)\n            return d->m_prev_return_status;\n        d->m_finished = (flush == TDEFL_FINISH);\n        if (flush == TDEFL_FULL_FLUSH)\n        {\n            MZ_CLEAR_OBJ(d->m_hash);\n            MZ_CLEAR_OBJ(d->m_next);\n            d->m_dict_size = 0;\n        }\n    }\n\n    return (d->m_prev_return_status = tdefl_flush_output_buffer(d));\n}\n\ntdefl_status tdefl_init(tdefl_compressor *d, int flags)\n{\n    d->m_flags = (mz_uint)(flags);\n    d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3;\n    d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;\n    d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;\n    if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))\n        MZ_CLEAR_OBJ(d->m_hash);\n    d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;\n    d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;\n    d->m_pLZ_code_buf = d->m_lz_code_buf + 1;\n    d->m_pLZ_flags = d->m_lz_code_buf;\n    d->m_num_flags_left = 8;\n    d->m_pOutput_buf = d->m_output_buf;\n    d->m_pOutput_buf_end = d->m_output_buf;\n    d->m_prev_return_status = TDEFL_STATUS_OKAY;\n    d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0;\n    d->m_adler32 = 1;\n    d->m_pIn_buf = NULL;\n    d->m_pOut_buf = NULL;\n    d->m_pIn_buf_size = NULL;\n    d->m_pOut_buf_size = NULL;\n    d->m_flush = TDEFL_NO_FLUSH;\n    d->m_pSrc = NULL;\n    d->m_src_buf_left = 0;\n    d->m_out_buf_ofs = 0;\n    memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);\n    memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);\n    return TDEFL_STATUS_OKAY;\n}\n\nstatic const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };\n\n// level may actually range from [0,10] (10 is a \"hidden\" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files).\nmz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)\n{\n    mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);\n    if (window_bits > 0)\n        comp_flags |= TDEFL_WRITE_ZLIB_HEADER;\n\n    if (!level)\n        comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;\n    else if (strategy == MZ_FILTERED)\n        comp_flags |= TDEFL_FILTER_MATCHES;\n    else if (strategy == MZ_HUFFMAN_ONLY)\n        comp_flags &= ~TDEFL_MAX_PROBES_MASK;\n    else if (strategy == MZ_FIXED)\n        comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;\n    else if (strategy == MZ_RLE)\n        comp_flags |= TDEFL_RLE_MATCHES;\n\n    return comp_flags;\n}\n\nmz_uint32 mz_adler32(mz_uint32 adler, const unsigned char *ptr, size_t buf_len)\n{\n    mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16);\n    size_t block_len = buf_len % 5552;\n    if (!ptr)\n        return MZ_ADLER32_INIT;\n    while (buf_len)\n    {\n        for (i = 0; i + 7 < block_len; i += 8, ptr += 8)\n        {\n            s1 += ptr[0], s2 += s1;\n            s1 += ptr[1], s2 += s1;\n            s1 += ptr[2], s2 += s1;\n            s1 += ptr[3], s2 += s1;\n            s1 += ptr[4], s2 += s1;\n            s1 += ptr[5], s2 += s1;\n            s1 += ptr[6], s2 += s1;\n            s1 += ptr[7], s2 += s1;\n        }\n        for (; i < block_len; ++i)\n            s1 += *ptr++, s2 += s1;\n        s1 %= 65521U, s2 %= 65521U;\n        buf_len -= block_len;\n        block_len = 5552;\n    }\n    return (s2 << 16) + s1;\n}\n"
  },
  {
    "path": "miniz_tdef.h",
    "content": "#pragma once\n\n// public domain code from https://github.com/richgel999/miniz/tree/42c29103c304a6e9201d000c96c31cd3031efc4f\n\n#include <string.h>\n#include <stdint.h>\n\n// ------------------- Types and macros\ntypedef uint8_t mz_uint8;\ntypedef int16_t mz_int16;\ntypedef uint16_t mz_uint16;\ntypedef uint32_t mz_uint32;\ntypedef uint32_t mz_uint;\ntypedef int64_t mz_int64;\ntypedef uint64_t mz_uint64;\ntypedef int mz_bool;\n\n#define MZ_FALSE (0)\n#define MZ_TRUE (1)\n\n// Works around MSVC's spammy \"warning C4127: conditional expression is constant\" message.\n#ifdef _MSC_VER\n#define MZ_MACRO_END while (0, 0)\n#else\n#define MZ_MACRO_END while (0)\n#endif\n\n#define MZ_ASSERT(x)\n\n#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b))\n#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b))\n#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))\n\n#ifdef _MSC_VER\n#define MZ_FORCEINLINE __forceinline\n#elif defined(__GNUC__)\n#define MZ_FORCEINLINE inline __attribute__((__always_inline__))\n#else\n#define MZ_FORCEINLINE inline\n#endif\n\n// ------------------- Low-level Compression API Definitions\n\n// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search):\n// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression).\nenum\n{\n    TDEFL_HUFFMAN_ONLY = 0,\n    TDEFL_DEFAULT_MAX_PROBES = 128,\n    TDEFL_MAX_PROBES_MASK = 0xFFF\n};\n\n// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data.\n// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers).\n// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing.\n// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory).\n// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1)\n// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled.\n// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables.\n// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks.\n// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK).\nenum\n{\n    TDEFL_WRITE_ZLIB_HEADER = 0x01000,\n    TDEFL_COMPUTE_ADLER32 = 0x02000,\n    TDEFL_GREEDY_PARSING_FLAG = 0x04000,\n    TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,\n    TDEFL_RLE_MATCHES = 0x10000,\n    TDEFL_FILTER_MATCHES = 0x20000,\n    TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000,\n    TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000\n};\n\nenum\n{\n    TDEFL_MAX_HUFF_TABLES = 3,\n    TDEFL_MAX_HUFF_SYMBOLS_0 = 288,\n    TDEFL_MAX_HUFF_SYMBOLS_1 = 32,\n    TDEFL_MAX_HUFF_SYMBOLS_2 = 19,\n    TDEFL_LZ_DICT_SIZE = 32768,\n    TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1,\n    TDEFL_MIN_MATCH_LEN = 3,\n    TDEFL_MAX_MATCH_LEN = 258\n};\n\nenum\n{\n    TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024,\n    TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10,\n    TDEFL_MAX_HUFF_SYMBOLS = 288,\n    TDEFL_LZ_HASH_BITS = 15,\n    TDEFL_LEVEL1_HASH_SIZE_MASK = 4095,\n    TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3,\n    TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS\n};\n\n// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions.\ntypedef enum\n{\n    TDEFL_STATUS_BAD_PARAM = -2,\n    TDEFL_STATUS_PUT_BUF_FAILED = -1,\n    TDEFL_STATUS_OKAY = 0,\n    TDEFL_STATUS_DONE = 1,\n} tdefl_status;\n\n// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums\ntypedef enum\n{\n    TDEFL_NO_FLUSH = 0,\n    TDEFL_SYNC_FLUSH = 2,\n    TDEFL_FULL_FLUSH = 3,\n    TDEFL_FINISH = 4\n} tdefl_flush;\n\n// tdefl's compression state structure.\ntypedef struct\n{\n    mz_uint m_flags, m_max_probes[2];\n    int m_greedy_parsing;\n    mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;\n    mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;\n    mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;\n    mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;\n    tdefl_status m_prev_return_status;\n    const void *m_pIn_buf;\n    void *m_pOut_buf;\n    size_t *m_pIn_buf_size, *m_pOut_buf_size;\n    tdefl_flush m_flush;\n    const mz_uint8 *m_pSrc;\n    size_t m_src_buf_left, m_out_buf_ofs;\n    mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];\n    mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];\n    mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];\n    mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];\n    mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];\n    mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];\n    mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];\n    mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];\n} tdefl_compressor;\n\n// Initializes the compressor.\n// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory.\n// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.)\ntdefl_status tdefl_init(tdefl_compressor *d, int flags);\n\n// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible.\ntdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);\n\n#define MZ_ADLER32_INIT (1)\n// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.\nmz_uint32 mz_adler32(mz_uint32 adler, const unsigned char *ptr, size_t buf_len);\n\n// Compression strategies.\nenum\n{\n    MZ_DEFAULT_STRATEGY = 0,\n    MZ_FILTERED = 1,\n    MZ_HUFFMAN_ONLY = 2,\n    MZ_RLE = 3,\n    MZ_FIXED = 4\n};\n\n// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL.\nenum\n{\n    MZ_NO_COMPRESSION = 0,\n    MZ_BEST_SPEED = 1,\n    MZ_BEST_COMPRESSION = 9,\n    MZ_UBER_COMPRESSION = 10,\n    MZ_DEFAULT_LEVEL = 6,\n    MZ_DEFAULT_COMPRESSION = -1\n};\n\n#define MZ_DEFAULT_WINDOW_BITS 15\n\n// Create tdefl_compress() flags given zlib-style compression parameters.\n// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files)\n// window_bits may be -15 (raw deflate) or 15 (zlib)\n// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED\nmz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);\n"
  },
  {
    "path": "pkg2zip.c",
    "content": "#include \"pkg2zip_aes.h\"\n#include \"pkg2zip_zip.h\"\n#include \"pkg2zip_out.h\"\n#include \"pkg2zip_psp.h\"\n#include \"pkg2zip_utils.h\"\n#include \"pkg2zip_zrif.h\"\n\n#include <assert.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#ifdef __GNUC__\n#pragma GCC diagnostic ignored \"-Wpragmas\"\n#pragma GCC diagnostic ignored \"-Wunknown-warning-option\"\n#pragma GCC diagnostic ignored \"-Wformat-truncation\"\n#endif\n\n#define PKG_HEADER_SIZE 192\n#define PKG_HEADER_EXT_SIZE 64\n\n#define VER \"2.5\"\n\n// https://wiki.henkaku.xyz/vita/Packages#AES_Keys\nstatic const uint8_t pkg_ps3_key[] = { 0x2e, 0x7b, 0x71, 0xd7, 0xc9, 0xc9, 0xa1, 0x4e, 0xa3, 0x22, 0x1f, 0x18, 0x88, 0x28, 0xb8, 0xf8 };\nstatic const uint8_t pkg_psp_key[] = { 0x07, 0xf2, 0xc6, 0x82, 0x90, 0xb5, 0x0d, 0x2c, 0x33, 0x81, 0x8d, 0x70, 0x9b, 0x60, 0xe6, 0x2b };\nstatic const uint8_t pkg_vita_2[] = { 0xe3, 0x1a, 0x70, 0xc9, 0xce, 0x1d, 0xd7, 0x2b, 0xf3, 0xc0, 0x62, 0x29, 0x63, 0xf2, 0xec, 0xcb };\nstatic const uint8_t pkg_vita_3[] = { 0x42, 0x3a, 0xca, 0x3a, 0x2b, 0xd5, 0x64, 0x9f, 0x96, 0x86, 0xab, 0xad, 0x6f, 0xd8, 0x80, 0x1f };\nstatic const uint8_t pkg_vita_4[] = { 0xaf, 0x07, 0xfd, 0x59, 0x65, 0x25, 0x27, 0xba, 0xf1, 0x33, 0x89, 0x66, 0x8b, 0x17, 0xd9, 0xea };\n\n// http://vitadevwiki.com/vita/System_File_Object_(SFO)_(PSF)#Internal_Structure\n// https://github.com/TheOfficialFloW/VitaShell/blob/1.74/sfo.h#L29\nstatic void parse_sfo_content(const uint8_t* sfo, uint32_t sfo_size, char* category, char* title, char* content, char* min_version, char* pkg_version, char* discid)\n{\n    if (get32le(sfo) != 0x46535000)\n    {\n        sys_error(\"ERROR: incorrect sfo signature\\n\");\n    }\n\n    uint32_t keys = get32le(sfo + 8);\n    uint32_t values = get32le(sfo + 12);\n    uint32_t count = get32le(sfo + 16);\n\n    int title_index = -1;\n    int content_index = -1;\n    int category_index = -1;\n    int minver_index = -1;\n    int pkgver_index = -1;\n    int discid_index = -1;\n\n    for (uint32_t i = 0; i < count; i++)\n    {\n        if (i * 16 + 20 + 2 > sfo_size)\n        {\n            sys_error(\"ERROR: sfo information is too small\\n\");\n        }\n\n        char* key = (char*)sfo + keys + get16le(sfo + i * 16 + 20);\n        if (strcmp(key, \"TITLE\") == 0)\n        {\n            if (title_index < 0)\n            {\n                title_index = (int)i;\n            }\n        }\n        else if (strcmp(key, \"STITLE\") == 0)\n        {\n            title_index = (int)i;\n        }\n        else if (strcmp(key, \"CONTENT_ID\") == 0)\n        {\n            content_index = (int)i;\n        }\n        else if (strcmp(key, \"CATEGORY\") == 0)\n        {\n            category_index = (int)i;\n        }\n        else if (strcmp(key, \"PSP2_DISP_VER\") == 0)\n        {\n            minver_index = (int)i;\n        }\n        else if (strcmp(key, \"APP_VER\") == 0)\n        {\n            pkgver_index = (int)i;\n        }\n        else if (strcmp(key, \"DISC_ID\") == 0)\n        {\n            discid_index = (int)i;\n        }\n    }\n\n    if (title_index < 0)\n    {\n        sys_error(\"ERROR: cannot find title from sfo file, pkg is probably corrupted\\n\");\n    }\n    char* value = (char*)sfo + values + get32le(sfo + title_index * 16 + 20 + 12);\n    size_t i;\n    size_t max = 255;\n    \n    if(title) {\n        for (i = 0; i<max && *value; i++, value++)\n        {\n            if ((*value >= 32 && *value < 127 && strchr(\"<>\\\"/\\\\|?*\", *value) == NULL) || (uint8_t)*value >= 128)\n            {\n                if (*value == ':')\n                {\n                    *title++ = ' ';\n                    *title++ = '-';\n                    max--;\n                }\n                else\n                {\n                    *title++ = *value;\n                }\n            }\n            else if (*value == 10)\n            {\n                *title++ = ' ';\n            }\n        }\n        *title = 0;\n    }\n    if (content_index >= 0 && content)\n    {\n        value = (char*)sfo + values + get32le(sfo + content_index * 16 + 20 + 12);\n        while (*value)\n        {\n            *content++ = *value++;\n        }\n        *content = 0;\n    }\n\n    if (category_index >= 0 && category)\n    {\n        value = (char*)sfo + values + get32le(sfo + category_index * 16 + 20 + 12);\n        while (*value)\n        {\n            *category++ = *value++;\n        }\n        *category = 0;\n    }\n    \n    if(discid_index >= 0 && discid){\n        value = (char*)sfo + values + get32le(sfo + discid_index * 16 + 20 + 12);\n        while (*value)\n        {\n            *discid++ = *value++;\n        }\n        *discid = 0;\n    }\n\n    if (minver_index >= 0 && min_version)\n    {\n        value = (char*)sfo + values + get32le(sfo + minver_index * 16 + 20 + 12);\n        if (*value == '0')\n        {\n            value++;\n        }\n        while (*value)\n        {\n            *min_version++ = *value++;\n        }\n        if (min_version[-1] == '0')\n        {\n            min_version[-1] = 0;\n        }\n        else\n        {\n            *min_version = 0;\n        }\n    }\n\n    if (pkgver_index >= 0 && pkg_version)\n    {\n        value = (char*)sfo + values + get32le(sfo + pkgver_index * 16 + 20 + 12);\n        if (*value == '0')\n        {\n            value++;\n        }\n        while (*value)\n        {\n            *pkg_version++ = *value++;\n        }\n        *pkg_version = 0;\n    }\n}\n\nstatic void parse_sfo(sys_file f, uint64_t sfo_offset, uint32_t sfo_size, char* category, char* title, char* content, char* min_version, char* pkg_version, char* discid)\n{\n    uint8_t sfo[16 * 1024];\n    if (sfo_size < 16)\n    {\n        sys_error(\"ERROR: sfo information is too small\\n\");\n    }\n    if (sfo_size > sizeof(sfo))\n    {\n        sys_error(\"ERROR: sfo information is too big, pkg file is probably corrupted\\n\");\n    }\n    sys_read(f, sfo_offset, sfo, sfo_size);\n\n    parse_sfo_content(sfo, sfo_size, category, title, content, min_version, pkg_version, discid);\n}\n\nstatic void find_psp_sfo(const aes128_key* key, const aes128_key* ps3_key, const uint8_t* iv, sys_file pkg, uint64_t pkg_size, uint64_t enc_offset, uint64_t items_offset, uint32_t item_count, char* category, char* title)\n{\n    for (uint32_t item_index = 0; item_index < item_count; item_index++)\n    {\n        uint8_t item[32];\n        uint64_t item_offset = items_offset + item_index * 32;\n        sys_read(pkg, enc_offset + item_offset, item, sizeof(item));\n        aes128_ctr_xor(key, iv, item_offset / 16, item, sizeof(item));\n\n        uint32_t name_offset = get32be(item + 0);\n        uint32_t name_size = get32be(item + 4);\n        uint64_t data_offset = get64be(item + 8);\n        uint64_t data_size = get64be(item + 16);\n        uint8_t psp_type = item[24];\n\n        assert(name_offset % 16 == 0);\n        assert(data_offset % 16 == 0);\n\n        if (pkg_size < enc_offset + name_offset + name_size ||\n            pkg_size < enc_offset + data_offset + data_size)\n        {\n            sys_error(\"ERROR: pkg file is too short, possibly corrupted\\n\");\n        }\n\n        const aes128_key* item_key = psp_type == 0x90 ? key : ps3_key;\n\n        char name[ZIP_MAX_FILENAME];\n        sys_read(pkg, enc_offset + name_offset, name, name_size);\n        aes128_ctr_xor(item_key, iv, name_offset / 16, (uint8_t*)name, name_size);\n        name[name_size] = 0;\n\n        if (strcmp(name, \"PARAM.SFO\") == 0)\n        {\n            uint8_t sfo[16 * 1024];\n            if (data_size < 16)\n            {\n                sys_error(\"ERROR: sfo information is too small\\n\");\n            }\n            if (data_size > sizeof(sfo))\n            {\n                sys_error(\"ERROR: sfo information is too big, pkg file is probably corrupted\\n\");\n            }\n\n            sys_read(pkg, enc_offset + data_offset, sfo, (uint32_t)data_size);\n            aes128_ctr_xor(item_key, iv, data_offset / 16, sfo, (uint32_t)data_size);\n\n            parse_sfo_content(sfo, (uint32_t)data_size, category, title, NULL, NULL, NULL, NULL);\n            return;\n        }\n    }\n}\n\nstatic void find_pbp_sfo(const aes128_key* key, const aes128_key* ps3_key, const uint8_t* iv, sys_file pkg, uint64_t pkg_size, uint64_t enc_offset, uint64_t items_offset, uint32_t item_count, char* discid)\n{\n    memset(discid, 0x00, 0x0A);\n    for (uint32_t item_index = 0; item_index < item_count; item_index++)\n    {\n        uint8_t item[32];\n        uint64_t item_offset = items_offset + item_index * 32;\n        sys_read(pkg, enc_offset + item_offset, item, sizeof(item));\n        aes128_ctr_xor(key, iv, item_offset / 16, item, sizeof(item));\n\n        uint32_t name_offset = get32be(item + 0);\n        uint32_t name_size = get32be(item + 4);\n        uint64_t data_offset = get64be(item + 8);\n        uint64_t data_size = get64be(item + 16);\n        uint8_t psp_type = item[24];\n\n        assert(name_offset % 16 == 0);\n        assert(data_offset % 16 == 0);\n\n        if (pkg_size < enc_offset + name_offset + name_size ||\n            pkg_size < enc_offset + data_offset + data_size)\n        {\n            sys_error(\"ERROR: pkg file is too short, possibly corrupted\\n\");\n        }\n\n        const aes128_key* item_key = psp_type == 0x90 ? key : ps3_key;\n\n        char name[ZIP_MAX_FILENAME];\n        sys_read(pkg, enc_offset + name_offset, name, name_size);\n        aes128_ctr_xor(item_key, iv, name_offset / 16, (uint8_t*)name, name_size);\n        name[name_size] = 0;\n        \n        if (strcmp(name, \"USRDIR/CONTENT/EBOOT.PBP\") == 0 || strcmp(name, \"USRDIR/CONTENT/PBOOT.PBP\") == 0 || strcmp(name, \"USRDIR/CONTENT/PARAM.PBP\") == 0)\n        {\n            uint8_t eboot_header[0x28];\n            uint8_t sfo[16 * 1024];\n            \n            sys_read(pkg, enc_offset + data_offset, eboot_header, sizeof(eboot_header));\n            aes128_ctr_xor(item_key, iv, data_offset / 16, eboot_header, sizeof(eboot_header));\n            if (data_size < 0x28)\n            {\n                sys_error(\"ERROR: eboot.pbp file is too small\\n\");\n            }\n            if (memcmp(eboot_header, \"\\x00PBP\", 4) != 0)\n            {\n                sys_error(\"ERROR: wrong eboot.pbp header signature!\\n\");\n            }\n            \n            uint32_t sfo_offset = get32le(eboot_header + 0x8);\n            uint32_t icon0_offset = get32le(eboot_header + 0xC);\n            uint32_t sfo_size = (icon0_offset - sfo_offset);\n            \n            // get sfo header aligned to aes block size\n            uint32_t sfo_offset_aligned = (sfo_offset - (sfo_offset % 16));\n            uint32_t sfo_offset_off = sfo_offset % 16;\n\n            if (sfo_size < 16)\n            {\n                sys_error(\"ERROR: sfo information is too small\\n\");\n            }\n            if (sfo_size > sizeof(sfo))\n            {\n                sys_error(\"ERROR: sfo information is too big, pkg file is probably corrupted\\n\");\n            }\n\n            sys_read(pkg, enc_offset + data_offset + sfo_offset_aligned, sfo, sfo_size + sfo_offset_off);\n            aes128_ctr_xor(item_key, iv, (data_offset + sfo_offset_aligned) / 16, sfo, sfo_size + sfo_offset_off);\n\n            parse_sfo_content(sfo + sfo_offset_off, sfo_size, NULL, NULL, NULL, NULL, NULL, discid);\n            return;\n        }        \n    }\n}\n\nstatic const char* get_region(const char* id) {\n    if (memcmp(id, \"NPEE\", 4) == 0 || memcmp(id, \"NPEF\", 4) == 0 || // PS1 EUR\n        memcmp(id, \"UCES\", 4) == 0 || memcmp(id, \"ULES\", 4) == 0 || // PSP EUR\n        memcmp(id, \"NPEG\", 4) == 0 || memcmp(id, \"NPEH\", 4) == 0 || // PSP EUR\n        memcmp(id, \"NPEX\", 4) == 0 || memcmp(id, \"NPEZ\", 4) == 0 || // PSP EUR\n        memcmp(id, \"PCSB\", 4) == 0 || memcmp(id, \"PCSF\", 4) == 0 || // PSV EUR\n        memcmp(id, \"NPOA\", 4) == 0) { // PSM EUR\n        return \"EUR\";\n    }\n    else if (memcmp(id, \"NPHI\", 4) == 0 || memcmp(id, \"NPHJ\", 4) == 0 || // PS1 ASA\n             memcmp(id, \"UCAS\", 4) == 0 || memcmp(id, \"ULAS\", 4) == 0 || // PSP ASA\n             memcmp(id, \"NPHG\", 4) == 0 || memcmp(id, \"NPHH\", 4) == 0 || // PSP ASA\n             memcmp(id, \"NPHZ\", 4) == 0 || // PSP ASA\n             memcmp(id, \"PCSD\", 4) == 0 || memcmp(id, \"PCSH\", 4) == 0 || // PSV ASA\n             memcmp(id, \"NPQA\", 4) == 0) { // PSM ASA\n        return \"ASA\";\n    }\n    else if (memcmp(id, \"NPJI\", 4) == 0 || memcmp(id, \"NPJJ\", 4) == 0 || // PS1 JPN\n             memcmp(id, \"UCJM\", 4) == 0 || memcmp(id, \"ULJM\", 4) == 0 || // PSP JPN\n             memcmp(id, \"UCJS\", 4) == 0 || memcmp(id, \"ULJS\", 4) == 0 || // PSP JPN\n             memcmp(id, \"UCJB\", 4) == 0 || memcmp(id, \"NPJG\", 4) == 0 || // PSP JPN\n             memcmp(id, \"NPJH\", 4) == 0 || // PSP JPN\n             memcmp(id, \"PCSC\", 4) == 0 || memcmp(id, \"PCSG\", 4) == 0 || // PSV JPN\n             memcmp(id, \"NPPA\", 4) == 0) { // PSM JPN\n        return \"JPN\";\n    }\n    else if (memcmp(id, \"NPUF\", 4) == 0 || memcmp(id, \"NPUI\", 4) == 0 || // PS1 USA\n             memcmp(id, \"NPUJ\", 4) == 0 || // PS1 USA\n             memcmp(id, \"UCUS\", 4) == 0 || memcmp(id, \"ULUS\", 4) == 0 || // PSP USA\n             memcmp(id, \"NPUG\", 4) == 0 || memcmp(id, \"NPUH\", 4) == 0 || // PSP USA\n             memcmp(id, \"NPUX\", 4) == 0 || memcmp(id, \"NPUZ\", 4) == 0 || // PSP USA\n             memcmp(id, \"PCSA\", 4) == 0 || memcmp(id, \"PCSE\", 4) == 0 || // PSV USA\n             memcmp(id, \"NPNA\", 4) == 0) { // PSM USA\n        return \"USA\";\n    }\n    else if (memcmp(id, \"UCKS\", 4) == 0 || memcmp(id, \"ULKS\", 4) == 0) { // PSP KOR\n        return \"KOR\";\n    }\n    else if (memcmp(id, \"PCSI\", 4) == 0 || memcmp(id, \"NPXS\", 4) == 0) { // PSV INT\n        return \"INT\";\n    }\n    else {\n        return \"UNK\";\n    }\n}\n\nvoid print_help(char* bin_name)\n{\n    sys_output(\"Parameters:\\n\");\n    sys_output(\"\\n\");\n    sys_output(\"-x|--extract       Extract only. No zip compression\\n\");\n    sys_output(\"-l|--list          Shows the package (sfo) name and exits\\n\");\n    sys_output(\"-b|--no-bgdl       Disable bgdl output for VITA Theme extraction\\n\");\n    sys_output(\"-q|--quiet         Do not output anything to stdout\\n\");\n    sys_output(\"-h|--help          Shows this help message\\n\");\n    sys_output(\"\\n\");\n    sys_output(\"PSP/PSX only options:\\n\");\n    sys_output(\"-c[NUM]            Create a *.CSO file instead of ISO. [NUM] is the compression ratio\\n\");\n    sys_output(\"-p|--psp           Extracts PSP files in their original EBOOT.PBP format\\n\");\n    sys_output(\"-d|--decrypt       Always decrypt PSP DLC/EDAT files\\n\");\n    sys_output(\"PSM only options:\\n\");\n\tsys_output(\"-a|--android-psm   Extract into PSM for Android format.\\n\");\n    sys_output(\"\\n\");\n    sys_output(\"Usage: %s [-x] [-c[N]] [-b] [-p] <file.pkg> [zRIF]\\n\", bin_name);\n}\n\ntypedef enum {\n    PKG_TYPE_VITA_APP,\n    PKG_TYPE_VITA_DLC,\n    PKG_TYPE_VITA_PATCH,\n    PKG_TYPE_VITA_PSM,\n    PKG_TYPE_VITA_THEME,\n    PKG_TYPE_PSP,\n    PKG_TYPE_PSP_THEME,\n    PKG_TYPE_PSX,\n    PKG_TYPE_PS3 // PKG type 1\n} pkg_type;\n\nint main(int argc, char* argv[])\n{\n    sys_output_init();\n\n    int zipped = 1;\n    int listing = 0;\n    int verbose = 1;\n    int cso = 0;\n    int pbp = 0;\n    int ddlc = 0;\n    int bgdl = 1;\n\tint android = 0;\n    const char* pkg_arg = NULL;\n    const char* zrif_arg = NULL;\n    for (int i = 1; i < argc; i++)\n    {\n        if (strcmp(argv[i], \"-x\") == 0 || strcmp(argv[i], \"--extract\") == 0)\n        {\n            zipped = 0;\n        }\n        else if (strcmp(argv[i], \"-l\") == 0 || strcmp(argv[i], \"--list\") == 0)\n        {\n            listing = 1;\n            verbose = 0;\n        }\n        else if (strncmp(argv[i], \"-c\", 2) == 0)\n        {\n            if (argv[i][2] != 0)\n            {\n                cso = atoi(argv[i] + 2);\n                cso = cso > 9 ? 9 : cso < 0 ? 0 : cso;\n            }\n        }\n        else if (strcmp(argv[i], \"-p\") == 0 || strcmp(argv[i], \"--psp\") == 0)\n        {\n            pbp = 1;\n        }\n        else if (strcmp(argv[i], \"-d\") == 0 || strcmp(argv[i], \"--decrypt\") == 0)\n        {\n            ddlc = 1;\n        }\n        else if (strcmp(argv[i], \"-b\") == 0 || strcmp(argv[i], \"--no-bgdl\") == 0)\n        {\n            bgdl = 0;\n        }\n        else if (strcmp(argv[i], \"-q\") == 0 || strcmp(argv[i], \"--quiet\") == 0)\n        {\n            verbose = 0;\n        }\n\t\telse if (strcmp(argv[i], \"-a\") == 0 || strcmp(argv[i], \"--android-psm\") == 0)\n        {\n            android = 1;\n        }\n        else if (strcmp(argv[i], \"-h\") == 0 || strcmp(argv[i], \"--help\") == 0)\n        {\n            sys_output(\"pkg2zip v\"VER\"\\n\");\n            sys_output(\"\\n\");\n            print_help(argv[0]);\n            exit(0);\n        }\n        else\n        {\n            if (pkg_arg != NULL )\n            {\n                if(strlen(argv[i]) != 0)\n                {\n                    zrif_arg = argv[i];\n                }\n                break;\n            }\n            else\n            {\n                pkg_arg = argv[i];\n            }\n        }\n    }\n    if (pkg_arg == NULL)\n    {\n        fprintf(stderr, \"ERROR: no pkg file specified\\n\");\n        print_help(argv[0]);\n        exit(1);\n    }\n\n    if (verbose)\n    {\n        sys_output(\"pkg2zip v\"VER\"\\n\");\n    }\n\n    if (verbose)\n    {\n        sys_output(\"[*] loading...\\n\");\n    }\n\n    uint64_t pkg_size;\n    sys_file pkg = sys_open(pkg_arg, &pkg_size);\n\n    uint8_t pkg_header[PKG_HEADER_SIZE + PKG_HEADER_EXT_SIZE];\n    sys_read(pkg, 0, pkg_header, sizeof(pkg_header));\n\n    //if (get32be(pkg_header) != 0x7f504b47 || get32be(pkg_header + PKG_HEADER_SIZE) != 0x7F657874)\n    if (get32be(pkg_header) != 0x7f504b47) // do not check for extended header\n    {\n        sys_error(\"ERROR: not a pkg file\\n\");\n    }\n\n    // https://www.psdevwiki.com/ps3/PKG_files#Header\n    \n    uint16_t hdr_type = get16be(pkg_header + 6);\n    uint64_t meta_offset = get32be(pkg_header + 8); // why uint64_t?\n    uint32_t meta_count = get32be(pkg_header + 12);\n    uint32_t item_count = get32be(pkg_header + 20);\n    uint64_t total_size = get64be(pkg_header + 24);\n    uint64_t enc_offset = get64be(pkg_header + 32);\n    uint64_t enc_size = get64be(pkg_header + 40);\n    const uint8_t* iv = pkg_header + 0x70;\n    int key_type = pkg_header[0xe7] & 7;\n\n    if (pkg_size < total_size)\n    {\n        sys_error(\"ERROR: pkg file is too small\\n\");\n    }\n    if (pkg_size < enc_offset + item_count * 32)\n    {\n        sys_error(\"ERROR: pkg file is too small\\n\");\n    }\n    \n    uint32_t content_type = 0;\n    uint32_t sfo_offset = 0;\n    uint32_t sfo_size = 0;\n    uint32_t items_offset = 0;\n    uint32_t items_size = 0;\n    char install_directory[0x28] = {0};\n\n    for (uint32_t i = 0; i < meta_count; i++)\n    {\n        uint8_t block[16];\n        sys_read(pkg, meta_offset, block, sizeof(block));\n\n        uint32_t type = get32be(block + 0);\n        uint32_t size = get32be(block + 4);\n\n        if (type == 2)\n        {\n            content_type = get32be(block + 8);\n        }\n        else if (type == 13)\n        {\n            items_offset = get32be(block + 8);\n            items_size = get32be(block + 12);\n        }\n        else if (type == 14)\n        {\n            sfo_offset = get32be(block + 8);\n            sfo_size = get32be(block + 12);\n        }\n        else if (type == 10)\n        {\n            sys_read(pkg, meta_offset + 8 + 8, install_directory,sizeof(install_directory));\n            //sys_output(\"[*] DLC Install Directory: %s\\n\", install_directory);\n        }\n\n        meta_offset += 2 * sizeof(uint32_t) + size;\n    }\n\n    pkg_type type;\n\n    // http://www.psdevwiki.com/ps3/PKG_files\n    if (content_type == 1)\n    {\n        type = PKG_TYPE_PS3;\n    }\n    else if (content_type == 6)\n    {\n        type = PKG_TYPE_PSX;\n    }\n    else if (content_type == 7 || content_type == 0xe || content_type == 0xf || content_type == 0x10)\n    {\n        // PSP & PSP-PCEngine & DLC / PSP-Go / PSP-Mini / PSP-NeoGeo \n        type = PKG_TYPE_PSP;\n    }\n    else if (content_type == 0x9)\n    {\n        type = PKG_TYPE_PSP_THEME;\n    }\n    else if (content_type == 0x15)\n    {\n        type = PKG_TYPE_VITA_APP;\n    }\n    else if (content_type == 0x16)\n    {\n        type = PKG_TYPE_VITA_DLC;\n    }\n    else if (content_type == 0x18 || content_type == 0x1d)\n    {\n        type = PKG_TYPE_VITA_PSM;\n    }\n    else if (content_type ==  0x1f)\n    {\n        type = PKG_TYPE_VITA_THEME;\n    }\n    else\n    {\n        sys_error(\"ERROR: unsupported content type 0x%x\\n\", content_type);\n    }\n\n    aes128_key ps3_key;\n    uint8_t main_key[16];\n    if (key_type == 1)\n    {\n        memcpy(main_key, pkg_psp_key, sizeof(main_key));\n        aes128_init(&ps3_key, pkg_ps3_key);\n    }\n    else if (key_type == 2)\n    {\n        aes128_key key;\n        aes128_init(&key, pkg_vita_2);\n        aes128_ecb_encrypt(&key, iv, main_key);\n    }\n    else if (key_type == 3)\n    {\n        aes128_key key;\n        aes128_init(&key, pkg_vita_3);\n        aes128_ecb_encrypt(&key, iv, main_key);\n    }\n    else if ((key_type == 4) && (hdr_type == 1)) // PKG_TYPE_PS3\n    {\n        memcpy(main_key, pkg_ps3_key, sizeof(main_key));\n        aes128_init(&ps3_key, pkg_ps3_key);\n    }\n    else if (key_type == 4)\n    {\n        aes128_key key;\n        aes128_init(&key, pkg_vita_4);\n        aes128_ecb_encrypt(&key, iv, main_key);\n    }\n\n    aes128_key key;\n    aes128_init(&key, main_key);\n    char discid[10];\n    char content[256];\n    char title[256];\n    char category[256];\n    char min_version[256];\n    char pkg_version[256];\n    const char* id = content + 7;\n    const char* id2 = id + 13;\n\n    // first 512 - for vita games - https://github.com/TheOfficialFloW/NoNpDrm/blob/v1.1/src/main.c#L42\n    // 1024 is used for PSM\n    uint8_t rif[1024];\n    uint32_t rif_size = 0;\n\n    if (type == PKG_TYPE_PS3) {\n        // there is no PARAM.SFO inside\n        id = (char*)pkg_header + 0x37;\n        sprintf(title, \"%s\", id);\n    }\n    else if (type == PKG_TYPE_PSP || type == PKG_TYPE_PSX)\n    {\n        find_psp_sfo(&key, &ps3_key, iv, pkg, pkg_size, enc_offset, items_offset, item_count, category, title);\n\t\t\n        // read discid from PARAM.SFO in EBOOT.PBP\n        find_pbp_sfo(&key, &ps3_key, iv, pkg, pkg_size, enc_offset, items_offset, item_count, discid);\n\t\t\n        id = discid;\n        if(strcmp(id, \"\") == 0)\n          id = (char*)pkg_header + 0x37;\n        \n        if (type == PKG_TYPE_PSX && zrif_arg != NULL)  //pocketstation pkg type is PSX\n        {\n            rif_size = 512;\n            zrif_decode(zrif_arg, rif, rif_size);\n        }\n    }\n    else if (type == PKG_TYPE_PSP_THEME)\n    {\n        id = (char*)pkg_header + 0x37;\n        memcpy(title, pkg_header + 0x44, 0x10);\n\n        uint8_t item[32];\n        sys_read(pkg, enc_offset + items_offset, item, sizeof(item));\n        aes128_ctr_xor(&key, iv, items_offset / 16, item, sizeof(item));\n\n        uint64_t data_offset = get64be(item + 8);\n        uint64_t data_size = get64be(item + 16);\n        uint8_t psp_type = item[24];\n\n        assert(data_offset % 16 == 0);\n\n        if (pkg_size < enc_offset + data_offset + data_size)\n        {\n            sys_error(\"ERROR: pkg file is too short, possibly corrupted\\n\");\n        }\n\n        const aes128_key* item_key;\n        item_key = psp_type == 0x90 ? &key : &ps3_key;\n        get_psp_theme_title(title, item_key, iv, pkg, enc_offset, data_offset);\n\n        //Theme names are prone to having colons\n        for (uint32_t i = 0; i < sizeof(title); i++)\n        {\n            if (title[i] == 58)\n            {\n                title[i] = 32;\n            }\n        }\n    }\n    else // Vita\n    {\n        if (type == PKG_TYPE_VITA_PSM)\n        {\n            memcpy(content, pkg_header + 0x30, 0x30);\n            rif_size = 1024;\n        }\n        else if (type == PKG_TYPE_VITA_THEME)\n        {\n            parse_sfo(pkg, sfo_offset, sfo_size, category, title, content, min_version, pkg_version, NULL);\n            rif_size = 512;\n        }\n        else // Vita APP, DLC or PATCH\n        {\n            parse_sfo(pkg, sfo_offset, sfo_size, category, title, content, min_version, pkg_version, NULL);\n            rif_size = 512;\n            \n            if (type == PKG_TYPE_VITA_APP && strcmp(category, \"gp\") == 0)\n            {\n                type = PKG_TYPE_VITA_PATCH;\n            }\n        }\n\n        if (type != PKG_TYPE_VITA_PATCH && zrif_arg != NULL)\n        {\n            zrif_decode(zrif_arg, rif, rif_size);\n            const char* rif_contentid = (char*)rif + (type == PKG_TYPE_VITA_PSM ? 0x50 : 0x10);\n            if (strncmp(rif_contentid, content, 0x30) != 0)\n            {\n                sys_error(\"ERROR: zRIF content id '%s' doesn't match pkg '%s'\\n\", rif_contentid, content);\n            }\n        }\n    }\n\n    const char* ext = zipped ? \".zip\" : \"\";\n\n    char root[1024];\n    if (type == PKG_TYPE_PSP)\n    {\n        const char* type_str;\n        if (content_type == 7)\n        {\n            type_str = (strcmp(category, \"HG\") == 0) ? \"PSP-PCEngine\" : install_directory[0] != 0 ? \"PSP-DLC\" :\"PSP\";\n        }\n        else\n        {\n            type_str = content_type == 0xe ? \"PSP-Go\" : content_type == 0xf ? \"PSP-Mini\" : \"PSP-NeoGeo\";\n        }\n        snprintf(root, sizeof(root), \"%s [%.9s] [%s]%s\", title, id, type_str, ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking %s\\n\", type_str);\n        }\n    }\n    else if (type == PKG_TYPE_PSP_THEME)\n    {\n        snprintf(root, sizeof(root), \"%s [%.9s] [PSP-Theme]%s\", title, id, ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking PSP Theme\\n\");\n        }\n    }\n    else if (type == PKG_TYPE_PSX)\n    {\n        snprintf(root, sizeof(root), \"%s [%.9s] [%s] [PSX]%s\", title, id, get_region(id), ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking PSX\\n\");\n        }\n    }\n    else if (type == PKG_TYPE_PS3)\n    {\n        snprintf(root, sizeof(root), \"%s [%.9s] [%s] [PSX]%s\", title, id, get_region(id), ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking PS3 PSX\\n\");\n        }\n    }\n    else if (type == PKG_TYPE_VITA_DLC)\n    {\n        snprintf(root, sizeof(root), \"%s [%.9s] [%s] [DLC-%s]%s\", title, id, get_region(id), id2, ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking Vita DLC\\n\");\n        }\n    }\n    else if (type == PKG_TYPE_VITA_PATCH)\n    {\n        snprintf(root, sizeof(root), \"%s [%.9s] [%s] [PATCH] [v%s]%s\", title, id, get_region(id), pkg_version, ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking Vita PATCH\\n\");\n        }\n    }\n    else if (type == PKG_TYPE_VITA_PSM)\n    {\n        snprintf(root, sizeof(root), \"%.9s [%s] [PSM]%s\", id, get_region(id), ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking Vita PSM\\n\");\n        }\n    }\n    else if (type == PKG_TYPE_VITA_APP)\n    {\n        snprintf(root, sizeof(root), \"%s [%.9s] [%s]%s\", title, id, get_region(id), ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking Vita APP\\n\");\n        }\n    }\n    else if (type == PKG_TYPE_VITA_THEME)\n    {\n        snprintf(root, sizeof(root), \"%s [%.9s] [%s]%s\", title, id, get_region(id), ext);\n        if (verbose)\n        {\n            sys_output(\"[*] unpacking Vita theme\\n\");\n        }\n    }\n    else\n    {\n        assert(0);\n        sys_error(\"ERROR: unsupported type %i\\n\", type);\n    }\n\n    if (listing && zipped)\n    {\n        sys_output(\"%s\\n\", root);\n        exit(0);\n    }\n    else if (listing && zipped == 0)\n    {\n        sys_error(\"ERROR: Listing option without creating zip is useless\\n\");\n    }\n\n    if (verbose)\n    {\n        if (zipped)\n            sys_output(\"[*] creating '%s' archive\\n\", root);\n        else\n            sys_output(\"[*] unpacking '%s' to directory\\n\", root);\n    }\n\n    out_begin(root, zipped);\n    root[0] = 0;\n\n    if (type == PKG_TYPE_PSP)\n    {\n        snprintf(root, sizeof(root), \"pspemu/PSP/GAME/%.9s\", id);\n    }\n    else if (type == PKG_TYPE_PSP_THEME)\n    {\n        snprintf(root, sizeof(root), \"pspemu/PSP/THEME\");\n    }\n    else if (type == PKG_TYPE_PSX)\n    {\n        snprintf(root, sizeof(root), \"pspemu/PSP/GAME/%.9s\", id);\n    }\n    else if (type == PKG_TYPE_VITA_DLC)\n    {\n        sys_vstrncat(root, sizeof(root), \"addcont\");\n        out_add_folder(root);\n\n        sys_vstrncat(root, sizeof(root), \"/%.9s\", id);\n        out_add_folder(root);\n\n        sys_vstrncat(root, sizeof(root), \"/%s\", id2);\n        out_add_folder(root);\n    }\n    else if (type == PKG_TYPE_VITA_PATCH)\n    {\n        sys_vstrncat(root, sizeof(root), \"patch\");\n        out_add_folder(root);\n\n        sys_vstrncat(root, sizeof(root), \"/%.9s\", id);\n        out_add_folder(root);\n    }\n    else if (type == PKG_TYPE_VITA_PSM)\n    {\n        sys_vstrncat(root, sizeof(root), \"psm\");\n        out_add_folder(root);\n\n        sys_vstrncat(root, sizeof(root), \"/%.9s\", id);\n        out_add_folder(root);\n    }\n    else if (type == PKG_TYPE_VITA_APP)\n    {\n        sys_vstrncat(root, sizeof(root), \"app\");\n        out_add_folder(root);\n\n        sys_vstrncat(root, sizeof(root), \"/%.9s\", id);\n        out_add_folder(root);\n    }\n    else if (type == PKG_TYPE_VITA_THEME)\n    {\n\n        if (bgdl == 1)\n        {\n            sys_vstrncat(root, sizeof(root), \"bgdl/t\");\n            out_add_folder(root);\n\n            uint32_t bgdl_task = 0;\n            char dir[1024] = {0};\n            if(zipped == 0)\n            {\n                do \n                {\n                    bgdl_task++;\n                    snprintf(dir, sizeof(dir), \"%s/%08x\",root, bgdl_task);\n                } \n                while (sys_test_dir(dir));\n            }\n            else\n            {\n                bgdl_task = 1;\n            }\n\n            sys_vstrncat(root,sizeof(root), \"/%08x\", bgdl_task);\n            out_add_folder(root);\n        }\n        else \n        {\n            sys_vstrncat(root, sizeof(root), \"app\");\n            out_add_folder(root);\n        }\n\n        sys_vstrncat(root, sizeof(root), \"/%.9s\", id);\n        out_add_folder(root);\n    }\n    else if (type == PKG_TYPE_PS3)\n    {\n        snprintf(root, sizeof(root), \"pspemu/PSP/GAME\");\n    }\n    else\n    {\n        assert(0);\n        sys_error(\"ERROR: unsupported type %i\\n\", type);\n    }\n\n    char path[1024];\n\n    sys_output_progress_init(pkg_size);\n\n\tchar rw_folder[1024];\n\tchar ro_folder[1024];\n\n\tif(type == PKG_TYPE_VITA_PSM) {\n\t\tif(android) {\n\t\t\tsnprintf(rw_folder, sizeof(rw_folder) - 1, \"%s\", root);\n\t\t\tsnprintf(ro_folder, sizeof(ro_folder) - 1, \"%s\", root);\n\t\t}\n\t\telse {\n\t\t\tsnprintf(rw_folder, sizeof(rw_folder) - 1, \"%s/RW\", root);\n\t\t\tsnprintf(ro_folder, sizeof(ro_folder) - 1, \"%s/RO\", root);\n\t\t}\t\t\t\n\t}\n\n    for (uint32_t item_index = 0; item_index < item_count; item_index++)\n    {\n        uint8_t item[32];\n        uint64_t item_offset = items_offset + item_index * 32;\n        sys_read(pkg, enc_offset + item_offset, item, sizeof(item));\n        aes128_ctr_xor(&key, iv, item_offset / 16, item, sizeof(item));\n\n        uint32_t name_offset = get32be(item + 0);\n        uint32_t name_size = get32be(item + 4);\n        uint64_t data_offset = get64be(item + 8);\n        uint64_t data_size = get64be(item + 16);\n        uint8_t psp_type = item[24];\n        uint8_t flags = item[27];\n\n        assert(name_offset % 16 == 0);\n        assert(data_offset % 16 == 0);\n\n        if (pkg_size < enc_offset + name_offset + name_size ||\n            pkg_size < enc_offset + data_offset + data_size)\n        {\n            sys_error(\"ERROR: pkg file is too short, possibly corrupted\\n\");\n        }\n\n        if (name_size >= ZIP_MAX_FILENAME)\n        {\n            sys_error(\"ERROR: pkg file contains file with very long name\\n\");\n        }\n\n        const aes128_key* item_key;\n        if (type == PKG_TYPE_PSP || type == PKG_TYPE_PSX || type == PKG_TYPE_PSP_THEME)\n        {\n            item_key = psp_type == 0x90 ? &key : &ps3_key;\n        }\n        else\n        {\n            item_key = &key;\n        }\n\n        char name[ZIP_MAX_FILENAME];\n        sys_read(pkg, enc_offset + name_offset, name, name_size);\n        aes128_ctr_xor(item_key, iv, name_offset / 16, (uint8_t*)name, name_size);\n        name[name_size] = 0;\n\n        // sys_output(\"[%u/%u] %s\\n\", item_index + 1, item_count, name);\n\n\t\t\n        if (flags == 4 || flags == 18) // Directory\n        {\n            if (type == PKG_TYPE_VITA_PSM)\n            {\n                // skip \"content/\" prefix\n                char* slash = strchr(name, '/');\n                if (slash != NULL)\n                {\n                    if (strstr(name, \"runtime\"))\n                    {\n                        snprintf(path, sizeof(path), \"%s/%s\", root, name + 9);\n                    }\n                    else\n                    {\n                        snprintf(path, sizeof(path), \"%s/%s\", ro_folder, name + 9);\n                    }\n                    out_add_folder(path);\n                }\n            }\n            else if (type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_DLC || type == PKG_TYPE_VITA_PATCH || type == PKG_TYPE_VITA_THEME)\n            {\n                snprintf(path, sizeof(path), \"%s/%s\", root, name);\n                out_add_folder(path);\n            }\n            // sys_output(\"dir : %s\\n\", path);\n        }\n        else // File\n        {\n            int decrypt = 1;\n            if ((type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_DLC || type == PKG_TYPE_VITA_PATCH || type == PKG_TYPE_VITA_THEME) && (strcmp(\"sce_sys/package/digs.bin\", name) == 0 || strcmp(\"sce_sys/package/cert.bin\", name) == 0 ))\n            {\n                snprintf(path, sizeof(path), \"%s/sce_sys/package\", root);\n                out_add_folder(path);\n                if (verbose)\n                {\n                    sys_output(\"[*] renaming %s to body.bin\\n\", name);\n                }\n                snprintf(name, sizeof(name), \"%s\", \"sce_sys/package/body.bin\");\n                decrypt = 0;\n            }\n\n            if (type == PKG_TYPE_PS3) {\n                char game[10]; // ABCD12345\n                strncpy(game, &name[0], 9);\n                game[9] = '\\0';\n                if (strstr(name, \"DOCUMENT.DAT\") != NULL) {\n                    sprintf(path, \"pspemu/PSP/GAME/%s/DOCUMENT.DAT\", game);\n                } else if (strstr(name, \"EBOOT.PBP\") != NULL) {\n                    sprintf(path, \"pspemu/PSP/GAME/%s\", game);\n                    out_add_folder(path);\n                    sprintf(path, \"pspemu/PSP/GAME/%s/KEYS.BIN\", game);\n                    unpack_keys_bin(path, item_key, iv, pkg, enc_offset, data_offset, data_size);\n                    sprintf(path, \"pspemu/PSP/GAME/%s/EBOOT.PBP\", game);\n                } else {\n                    continue;\n                }\n            }\n            else if (type == PKG_TYPE_PSX)\n            {\n                if (strcmp(\"USRDIR/CONTENT/DOCUMENT.DAT\", name) == 0)\n                {\n                    snprintf(path, sizeof(path), \"pspemu/PSP/GAME/%.9s/DOCUMENT.DAT\", id);\n                }\n                else if (strcmp(\"USRDIR/CONTENT/EBOOT.PBP\", name) == 0)\n                {\n                    snprintf(path, sizeof(path), \"pspemu/PSP/GAME/%.9s/KEYS.BIN\", id);\n                    unpack_keys_bin(path, item_key, iv, pkg, enc_offset, data_offset, data_size);\n                    snprintf(path, sizeof(path), \"pspemu/PSP/GAME/%.9s/EBOOT.PBP\", id);\n                }\n                else if (strcmp(\"USRDIR/CONTENT/texture.enc\", name) == 0)\n                {\n                    snprintf(path, sizeof(path), \"ps1emu/%.9s/texture.enc\", id);\n                }\n                else\n                {\n                    continue;\n                }\n            }\n            else if (type == PKG_TYPE_PSP)\n            {\n                if (strcmp(\"USRDIR/CONTENT/EBOOT.PBP\", name) == 0)\n                {\n                    snprintf(path, sizeof(path), \"pspemu/PSP/GAME/%.9s/EBOOT.PBP\", id);\n                    if (!pbp)\n                    {\n                        snprintf(path, sizeof(path), \"pspemu/ISO/%s [%.9s].%s\", title, id, cso ? \"cso\" : \"iso\");\n                        out_add_parent(path);\n                        unpack_psp_eboot(path, item_key, iv, pkg, enc_offset, data_offset, data_size, cso);\n                        continue;\n                    }\n                }\n                else if (strcmp(\"USRDIR/CONTENT/DOCUMENT.DAT\", name) == 0)\n                {\n                    snprintf(path, sizeof(path), \"pspemu/PSP/GAME/%.9s/DOCUMENT.DAT\", id);\n                    if (!pbp)\n                    {\n                        continue;\n                    }\n                }\n                else if (strcmp(\"USRDIR/CONTENT/DOCINFO.EDAT\", name) == 0)\n                {\n                    snprintf(path, sizeof(path), \"pspemu/PSP/GAME/%.9s/DOCINFO.EDAT\", id);\n                    if (!pbp)\n                    {\n                        continue;\n                    }\n                }\n                else if (strcmp(\"USRDIR/CONTENT/PSP-KEY.EDAT\", name) == 0)\n                {\n                    snprintf(path, sizeof(path), \"pspemu/PSP/GAME/%.9s/PSP-KEY.EDAT\", id);\n                    if (!pbp || ddlc)\n                    {\n                        out_add_parent(path);\n                        unpack_psp_key(path, item_key, iv, pkg, enc_offset, data_offset, data_size);                        \n                        continue;\n                    }\n                }\n                else if (strstr(name, \"USRDIR/CONTENT\"))\n                {\n                    // skip \"USRDIR/CONTENT\" prefix\n                    char* slash = strchr(name+14, '/');\n                    if (slash != NULL)\n                    {\n                        snprintf(path, sizeof(path), \"pspemu/PSP/GAME/%.9s/%s\", id, name+15);\n                        char* edat = strrchr(name, '.');\n                        if (edat != NULL)\n                        {\n                            if (strcmp(edat, \".edat\") == 0 || strcmp(edat, \".EDAT\") == 0)\n                            {\n                                if (!pbp || ddlc)\n                                {\n                                    out_add_parent(path);\n                                    unpack_psp_edat(path, item_key, iv, pkg, enc_offset, data_offset, data_size);\n                                    continue;\n                                }\n                            }\n                        }\n                    }\n                    else\n                    {\n                        continue;\n                    }\n                }\n                else\n                {\n                    continue;\n                }\n            }\n            else if (type == PKG_TYPE_PSP_THEME)\n            {\n                snprintf(path, sizeof(path), \"pspemu/PSP/THEME/%s\", name);\n                out_add_parent(path);\n                unpack_psp_edat(path, item_key, iv, pkg, enc_offset, data_offset, data_size);\n                continue;\n            }\n            else if (type == PKG_TYPE_VITA_PSM)\n            {\n                // skip \"content/\" prefix\n                if (strstr(name, \"runtime\"))\n                {\n                   snprintf(path, sizeof(path), \"%s/%s\", root, name + 9);\n                }\n                else\n                {\n                   snprintf(path, sizeof(path), \"%s/%s\", ro_folder, name + 9);\n                }\n            }\n            else\n            {\n                snprintf(path, sizeof(path), \"%s/%s\", root, name);\n            }\n\n            uint64_t offset = data_offset;\n            out_add_parent(path);\n            out_begin_file(path, 0);\n            while (data_size != 0)\n            {\n                uint8_t PKG_ALIGN(16) buffer[1 << 16];\n                uint32_t size = (uint32_t)min64(data_size, sizeof(buffer));\n                sys_output_progress(enc_offset + offset);\n                sys_read(pkg, enc_offset + offset, buffer, size);\n\n                if (decrypt)\n                {\n                    aes128_ctr_xor(item_key, iv, offset / 16, buffer, size);\n                }\n\n                out_write(buffer, size);\n                offset += size;\n                data_size -= size;\n            }\n            out_end_file();\n        }\n    }\n\n    if (verbose)\n    {\n        if (zipped)\n            sys_output(\"[*] creating completed\\n\");\n        else\n            sys_output(\"[*] unpacking completed\\n\");\n    }\n\n    if (type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_DLC || type == PKG_TYPE_VITA_PATCH || type == PKG_TYPE_VITA_THEME)\n    {\n        if (verbose)\n        {\n            sys_output(\"[*] creating sce_sys/package/head.bin\\n\");\n        }\n        snprintf(path, sizeof(path), \"%s/sce_sys/package/head.bin\", root);\n        out_add_parent(path);\n\n        out_begin_file(path, 0);\n        uint64_t head_size = enc_offset + items_size;\n        uint64_t head_offset = 0;\n        while (head_size != 0)\n        {\n            uint8_t PKG_ALIGN(16) buffer[1 << 16];\n            uint32_t size = (uint32_t)min64(head_size, sizeof(buffer));\n            sys_read(pkg, head_offset, buffer, size);\n            out_write(buffer, size);\n            head_size -= size;\n            head_offset += size;\n        }\n        out_end_file();\n\n        if (verbose)\n        {\n            sys_output(\"[*] creating sce_sys/package/tail.bin\\n\");\n        }\n        snprintf(path, sizeof(path), \"%s/sce_sys/package/tail.bin\", root);\n\n        out_begin_file(path, 0);\n        uint64_t tail_offset = enc_offset + enc_size;\n        while (tail_offset != pkg_size)\n        {\n            uint8_t PKG_ALIGN(16) buffer[1 << 16];\n            uint32_t size = (uint32_t)min64(pkg_size - tail_offset, sizeof(buffer));\n            sys_read(pkg, tail_offset, buffer, size);\n            out_write(buffer, size);\n            tail_offset += size;\n        }\n        out_end_file();\n\n        if (verbose)\n        {\n            sys_output(\"[*] creating sce_sys/package/stat.bin\\n\");\n        }\n        snprintf(path, sizeof(path), \"%s/sce_sys/package/stat.bin\", root);\n\n        uint8_t stat[768] = { 0 };\n        out_begin_file(path, 0);\n        out_write(stat, sizeof(stat));\n        out_end_file();\n    }\n\n    if ((type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_DLC || type == PKG_TYPE_VITA_PSM || type == PKG_TYPE_VITA_THEME || type == PKG_TYPE_PSX) && zrif_arg != NULL)\n    {\n        if (type == PKG_TYPE_VITA_PSM)\n        {\n            if (verbose)\n            {\n                sys_output(\"[*] creating %s/License\\n\", ro_folder);\n            }\n            snprintf(path, sizeof(path), \"%s/License\", ro_folder);\n            out_add_folder(path);\n\n            if (verbose)\n            {\n                sys_output(\"[*] creating %s/License/FAKE.rif\\n\", ro_folder);\n            }\n            snprintf(path, sizeof(path), \"%s/License/FAKE.rif\", ro_folder);\n        }\n        else if (type == PKG_TYPE_PSX)\n        {\n            //For Pocketstation\n            if (verbose)\n            {\n                sys_output(\"[*] creating rif\");\n            }\n            const char* rif_contentid = (char*)rif + 0x10;\n            snprintf(path, sizeof(path), \"pspemu/PSP/LICENSE\");\n            out_add_folder(path);\n            snprintf(path, sizeof(path), \"pspemu/PSP/LICENSE/%s.rif\", rif_contentid);\n        }\n        else\n        {\n            if (verbose)\n            {\n                sys_output(\"[*] creating sce_sys/package/work.bin\\n\");\n            }\n            snprintf(path, sizeof(path), \"%s/sce_sys/package/work.bin\", root);\n        }\n\n        out_begin_file(path, 0);\n        out_write(rif, rif_size);\n        out_end_file();\n    }\n\n    if (type == PKG_TYPE_VITA_THEME && bgdl == 1)\n    {\n        //get the parent directory\n        char* lastslash = strrchr(root, '/');\n        if (lastslash != NULL)\n        {\n            root[strlen(root)-strlen(lastslash)] = 0;\n        }\n        uint8_t pdb[0x200] = { 0 };\n        //PDB entries :: https://www.psdevwiki.com/ps3/Project_Database_(PDB)\n        memcpy(pdb+0x04,\"\\x64\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",0x10);\n        memcpy(pdb+0x14,\"\\x65\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",0x10); // 02 in d0.pdb, 00 in d1.pdb \n        memcpy(pdb+0x24,\"\\x66\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\",0x0D);             // \"Task unregister auto\"\n        memcpy(pdb+0x31,\"\\x68\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",0x10); // unknown but required\n        memcpy(pdb+0x41,\"\\x6B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\",0x10); // \"Task Subtype\"\n        memcpy(pdb+0x51,\"\\x6C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x01\\x00\\x00\\x00\",0x10); // unknown but required \n        memcpy(pdb+0x61,\"\\x6D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\",0x10); // unknown but required \n        memcpy(pdb+0x71,\"\\x6E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\",0x0D);\n        memcpy(pdb+0x7E,\"\\x6F\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",0x10); // unknown but required\n        memcpy(pdb+0x8E,\"\\x70\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\",0x0D);\n        memcpy(pdb+0x9B,\"\\x71\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\",0x0D);\n        memcpy(pdb+0xA8,\"\\x72\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",0x10);\n        memcpy(pdb+0xB8,\"\\x73\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\",0x0D);\n        memcpy(pdb+0xC5,\"\\x74\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\",0x0D);\n        memcpy(pdb+0xD2,\"\\x69\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x44\\x00\\x00\\x00\",0x0C);                 //pkg title\n        memcpy(pdb+0xDE,title,0x40);\n        memcpy(pdb+0x122,\"\\xD9\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x25\\x00\\x00\\x00\",0x0C);                 //ContentID\n        memcpy(pdb+0x12E,id,0x25);\n        memcpy(pdb+0x153,\"\\xDA\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\",0x0D);            // Download Complete Flag\n        memcpy(pdb+0x160,\"\\xDC\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\",0x0C);                //Content ID \n        memcpy(pdb+0x16C,id,0x09);\n\n        pdb[0x20] = 0x02;\n        if (verbose)\n        {\n            sys_output(\"[*] creating d0.pdb\\n\");\n        }\n        snprintf(path, sizeof(path), \"%s/d0.pdb\", root);\n        out_begin_file(path,0);\n        out_write(pdb,sizeof(pdb));\n        out_end_file();\n\n        pdb[0x20] = 0x00;\n        if (verbose)\n        {\n            sys_output(\"[*] creating d1.pdb\\n\");\n        }\n        snprintf(path, sizeof(path), \"%s/d1.pdb\", root);\n        out_begin_file(path,0);\n        out_write(pdb,sizeof(pdb));\n        out_end_file();\n\n        if (verbose)\n        {\n            sys_output(\"[*] creating f0.pdb\\n\");\n        }\n        snprintf(path, sizeof(path), \"%s/f0.pdb\", root);\n        out_begin_file(path,0);\n        out_write(pdb,0);\n        out_end_file();\n    }\n\n    if (type == PKG_TYPE_VITA_PSM)\n    {\n\t\t\n        if (verbose)\n        {\n            sys_output(\"[*] creating %s\\n\", rw_folder);\n        }\n        snprintf(path, sizeof(path), \"%s\", rw_folder);\n        out_add_folder(path);\n\n        if (verbose)\n        {\n            sys_output(\"[*] creating %s/Documents\\n\", rw_folder);\n        }\n        snprintf(path, sizeof(path), \"%s/Documents\", rw_folder);\n        out_add_folder(path);\n\n        if (verbose)\n        {\n            sys_output(\"[*] creating %s/Temp\\n\", rw_folder);\n        }\n        snprintf(path, sizeof(path), \"%s/Temp\", rw_folder);\n        out_add_folder(path);\n\n        if (verbose)\n        {\n            sys_output(\"[*] creating %s/System\\n\", rw_folder);\n        }\n        snprintf(path, sizeof(path), \"%s/System\", rw_folder);\n        out_add_folder(path);\n\n\t\tif(!android){\n\t\t\tif (verbose)\n\t\t\t{\n\t\t\t\tsys_output(\"[*] creating %s/System/content_id\\n\", rw_folder);\n\t\t\t}\n\t\t\t\n\t\t\tsnprintf(path, sizeof(path), \"%s/System/content_id\", rw_folder);\n\t\t\tout_begin_file(path, 0);\n\t\t\tout_write(pkg_header + 0x30, 0x30);\n\t\t\tout_end_file();\t\t\t\n\t\t}\n\n        if (verbose)\n        {\n            sys_output(\"[*] creating %s/System/pm.dat\\n\", rw_folder);\n        }\n        snprintf(path, sizeof(path), \"%s/System/pm.dat\", rw_folder);\n\n        uint8_t pm[1 << 16] = { 0 };\n        out_begin_file(path, 0);\n        out_write(pm, sizeof(pm));\n        out_end_file();\n    }\n\n    out_end();\n\n    if (verbose)\n    {\n        if (type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_PATCH)\n        {\n            sys_output(\"[*] minimum fw version required: %s\\n\", min_version);\n        }\n        sys_output(\"[*] done!\\n\");\n    }\n    sys_output_done();\n}\n"
  },
  {
    "path": "pkg2zip_aes.c",
    "content": "#include \"pkg2zip_aes.h\"\n#include \"pkg2zip_utils.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#if defined(_MSC_VER)\n#define PLATFORM_SUPPORTS_AESNI 1\n\n#include <intrin.h>\nstatic void get_cpuid(uint32_t level, uint32_t* arr)\n{\n    __cpuidex((int*)arr, level, 0);\n}\n\n#elif defined(__x86_64__) || defined(__i386__)\n#define PLATFORM_SUPPORTS_AESNI 1\n\n#include <cpuid.h>\nstatic void get_cpuid(uint32_t level, uint32_t* arr)\n{\n    __cpuid_count(level, 0, arr[0], arr[1], arr[2], arr[3]);\n}\n\n#else\n#define PLATFORM_SUPPORTS_AESNI 0\n#endif\n\n#if PLATFORM_SUPPORTS_AESNI\nstatic int aes128_supported_x86()\n{\n    static int init = 0;\n    static int supported;\n    if (!init)\n    {\n        init = 1;\n\n        uint32_t a[4];\n        get_cpuid(0, a);\n\n        if (a[0] >= 1)\n        {\n            get_cpuid(1, a);\n            supported = ((a[2] & (1 << 9)) && (a[2] & (1 << 25)));\n        }\n    }\n    return supported;\n}\n\nvoid aes128_init_x86(aes128_key* context, const uint8_t* key);\nvoid aes128_init_dec_x86(aes128_key* context, const uint8_t* key);\nvoid aes128_ecb_encrypt_x86(const aes128_key* context, const uint8_t* input, uint8_t* output);\nvoid aes128_ecb_decrypt_x86(const aes128_key* context, const uint8_t* input, uint8_t* output);\nvoid aes128_ctr_xor_x86(const aes128_key* context, const uint8_t* iv, uint8_t* buffer, size_t size);\nvoid aes128_cmac_process_x86(const aes128_key* ctx, uint8_t* block, const uint8_t *buffer, uint32_t size);\nvoid aes128_psp_decrypt_x86(const aes128_key* ctx, const uint8_t* prev, const uint8_t* block, uint8_t* buffer, uint32_t size);\n#endif\n\nstatic const uint8_t rcon[] = {\n    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36,\n};\n\nstatic const uint8_t Te[] = {\n    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,\n    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,\n    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,\n    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,\n    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,\n    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,\n    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,\n    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,\n    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,\n    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,\n    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,\n    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,\n    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,\n    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,\n    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,\n    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,\n};\n\nstatic uint8_t Td[] = {\n    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,\n    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,\n    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,\n    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,\n    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,\n    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,\n    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,\n    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,\n    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,\n    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,\n    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,\n    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,\n    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,\n    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,\n    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,\n    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,\n};\n\nstatic const uint32_t TE[] = {\n    0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,\n    0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,\n    0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,\n    0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,\n    0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,\n    0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,\n    0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,\n    0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,\n    0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,\n    0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,\n    0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,\n    0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,\n    0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,\n    0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,\n    0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,\n    0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,\n    0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,\n    0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,\n    0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,\n    0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,\n    0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,\n    0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,\n    0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,\n    0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,\n    0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,\n    0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,\n    0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,\n    0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,\n    0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,\n    0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,\n    0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,\n    0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,\n};\n\nstatic const uint32_t TD[] = {\n    0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,\n    0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,\n    0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,\n    0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,\n    0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,\n    0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,\n    0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,\n    0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,\n    0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,\n    0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,\n    0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,\n    0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,\n    0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,\n    0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,\n    0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,\n    0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,\n    0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,\n    0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,\n    0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,\n    0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,\n    0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,\n    0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,\n    0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,\n    0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,\n    0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,\n    0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,\n    0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,\n    0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,\n    0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,\n    0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,\n    0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,\n    0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742,\n};\n\nstatic uint8_t byte32(uint32_t x, int n)\n{\n    return (uint8_t)(x >> (8 * n));\n}\n\nstatic uint32_t ror32(uint32_t x, int n)\n{\n    return (x >> n) | (x << (32 - n));\n}\n\nstatic uint32_t setup_mix(uint32_t x)\n{\n    return (Te[byte32(x, 2)] << 24) ^ (Te[byte32(x, 1)] << 16) ^ (Te[byte32(x, 0)] << 8) ^ Te[byte32(x, 3)];\n}\n\nstatic uint32_t setup_mix2(uint32_t x)\n{\n    return TD[Te[byte32(x, 3)]] ^ ror32(TD[Te[byte32(x, 2)]], 8) ^ ror32(TD[Te[byte32(x, 1)]], 16) ^ ror32(TD[Te[byte32(x, 0)]], 24);\n}\n\nvoid aes128_init(aes128_key* ctx, const uint8_t* key)\n{\n#if PLATFORM_SUPPORTS_AESNI\n    if (aes128_supported_x86())\n    {\n        aes128_init_x86(ctx, key);\n        return;\n    }\n#endif\n\n    uint32_t* ekey = ctx->key;\n\n    ekey[0] = get32be(key +  0);\n    ekey[1] = get32be(key +  4);\n    ekey[2] = get32be(key +  8);\n    ekey[3] = get32be(key + 12);\n\n    for (size_t i=0; i<10; i++)\n    {\n        uint32_t temp = ekey[3];\n        ekey[4] = ekey[0] ^ setup_mix(temp) ^ (rcon[i] << 24);\n        ekey[5] = ekey[1] ^ ekey[4];\n        ekey[6] = ekey[2] ^ ekey[5];\n        ekey[7] = ekey[3] ^ ekey[6];\n        ekey += 4;\n    }\n}\n\nvoid aes128_init_dec(aes128_key* ctx, const uint8_t* key)\n{\n#if PLATFORM_SUPPORTS_AESNI\n    if (aes128_supported_x86())\n    {\n        aes128_init_dec_x86(ctx, key);\n        return;\n    }\n#endif\n\n    aes128_key enc;\n    aes128_init(&enc, key);\n\n    uint32_t* ekey = enc.key + 40;\n    uint32_t* dkey = ctx->key;\n\n    *dkey++ = ekey[0];\n    *dkey++ = ekey[1];\n    *dkey++ = ekey[2];\n    *dkey++ = ekey[3];\n    ekey -= 4;\n\n    for (size_t i = 0; i < 9; i++)\n    {\n        *dkey++ = setup_mix2(ekey[0]);\n        *dkey++ = setup_mix2(ekey[1]);\n        *dkey++ = setup_mix2(ekey[2]);\n        *dkey++ = setup_mix2(ekey[3]);\n        ekey -= 4;\n    }\n\n    *dkey++ = ekey[0];\n    *dkey++ = ekey[1];\n    *dkey++ = ekey[2];\n    *dkey++ = ekey[3];\n}\n\nstatic void aes128_encrypt(const aes128_key* ctx, const uint8_t* input, uint8_t* output)\n{\n    uint32_t t0, t1, t2, t3;\n    const uint32_t* key = ctx->key;\n\n    uint32_t s0 = get32be(input + 0) ^ *key++;\n    uint32_t s1 = get32be(input + 4) ^ *key++;\n    uint32_t s2 = get32be(input + 8) ^ *key++;\n    uint32_t s3 = get32be(input + 12) ^ *key++;\n\n    for (size_t i = 0; i<4; i++)\n    {\n        t0 = TE[byte32(s0, 3)] ^ ror32(TE[byte32(s1, 2)], 8) ^ ror32(TE[byte32(s2, 1)], 16) ^ ror32(TE[byte32(s3, 0)], 24) ^ *key++;\n        t1 = TE[byte32(s1, 3)] ^ ror32(TE[byte32(s2, 2)], 8) ^ ror32(TE[byte32(s3, 1)], 16) ^ ror32(TE[byte32(s0, 0)], 24) ^ *key++;\n        t2 = TE[byte32(s2, 3)] ^ ror32(TE[byte32(s3, 2)], 8) ^ ror32(TE[byte32(s0, 1)], 16) ^ ror32(TE[byte32(s1, 0)], 24) ^ *key++;\n        t3 = TE[byte32(s3, 3)] ^ ror32(TE[byte32(s0, 2)], 8) ^ ror32(TE[byte32(s1, 1)], 16) ^ ror32(TE[byte32(s2, 0)], 24) ^ *key++;\n\n        s0 = TE[byte32(t0, 3)] ^ ror32(TE[byte32(t1, 2)], 8) ^ ror32(TE[byte32(t2, 1)], 16) ^ ror32(TE[byte32(t3, 0)], 24) ^ *key++;\n        s1 = TE[byte32(t1, 3)] ^ ror32(TE[byte32(t2, 2)], 8) ^ ror32(TE[byte32(t3, 1)], 16) ^ ror32(TE[byte32(t0, 0)], 24) ^ *key++;\n        s2 = TE[byte32(t2, 3)] ^ ror32(TE[byte32(t3, 2)], 8) ^ ror32(TE[byte32(t0, 1)], 16) ^ ror32(TE[byte32(t1, 0)], 24) ^ *key++;\n        s3 = TE[byte32(t3, 3)] ^ ror32(TE[byte32(t0, 2)], 8) ^ ror32(TE[byte32(t1, 1)], 16) ^ ror32(TE[byte32(t2, 0)], 24) ^ *key++;\n    }\n\n    t0 = TE[byte32(s0, 3)] ^ ror32(TE[byte32(s1, 2)], 8) ^ ror32(TE[byte32(s2, 1)], 16) ^ ror32(TE[byte32(s3, 0)], 24) ^ *key++;\n    t1 = TE[byte32(s1, 3)] ^ ror32(TE[byte32(s2, 2)], 8) ^ ror32(TE[byte32(s3, 1)], 16) ^ ror32(TE[byte32(s0, 0)], 24) ^ *key++;\n    t2 = TE[byte32(s2, 3)] ^ ror32(TE[byte32(s3, 2)], 8) ^ ror32(TE[byte32(s0, 1)], 16) ^ ror32(TE[byte32(s1, 0)], 24) ^ *key++;\n    t3 = TE[byte32(s3, 3)] ^ ror32(TE[byte32(s0, 2)], 8) ^ ror32(TE[byte32(s1, 1)], 16) ^ ror32(TE[byte32(s2, 0)], 24) ^ *key++;\n\n    s0 = (Te[byte32(t0, 3)] << 24) ^ (Te[byte32(t1, 2)] << 16) ^ (Te[byte32(t2, 1)] << 8) ^ Te[byte32(t3, 0)] ^ *key++;\n    s1 = (Te[byte32(t1, 3)] << 24) ^ (Te[byte32(t2, 2)] << 16) ^ (Te[byte32(t3, 1)] << 8) ^ Te[byte32(t0, 0)] ^ *key++;\n    s2 = (Te[byte32(t2, 3)] << 24) ^ (Te[byte32(t3, 2)] << 16) ^ (Te[byte32(t0, 1)] << 8) ^ Te[byte32(t1, 0)] ^ *key++;\n    s3 = (Te[byte32(t3, 3)] << 24) ^ (Te[byte32(t0, 2)] << 16) ^ (Te[byte32(t1, 1)] << 8) ^ Te[byte32(t2, 0)] ^ *key++;\n\n    set32be(output + 0, s0);\n    set32be(output + 4, s1);\n    set32be(output + 8, s2);\n    set32be(output + 12, s3);\n}\n\nstatic void aes128_decrypt(const aes128_key* ctx, const uint8_t* input, uint8_t* output)\n{\n    const uint32_t* key = ctx->key;\n\n    uint32_t s0 = get32be(input + 0) ^ *key++;\n    uint32_t s1 = get32be(input + 4) ^ *key++;\n    uint32_t s2 = get32be(input + 8) ^ *key++;\n    uint32_t s3 = get32be(input + 12) ^ *key++;\n\n    uint32_t t0 = TD[byte32(s0, 3)] ^ ror32(TD[byte32(s3, 2)], 8) ^ ror32(TD[byte32(s2, 1)], 16) ^ ror32(TD[byte32(s1, 0)], 24) ^ *key++;\n    uint32_t t1 = TD[byte32(s1, 3)] ^ ror32(TD[byte32(s0, 2)], 8) ^ ror32(TD[byte32(s3, 1)], 16) ^ ror32(TD[byte32(s2, 0)], 24) ^ *key++;\n    uint32_t t2 = TD[byte32(s2, 3)] ^ ror32(TD[byte32(s1, 2)], 8) ^ ror32(TD[byte32(s0, 1)], 16) ^ ror32(TD[byte32(s3, 0)], 24) ^ *key++;\n    uint32_t t3 = TD[byte32(s3, 3)] ^ ror32(TD[byte32(s2, 2)], 8) ^ ror32(TD[byte32(s1, 1)], 16) ^ ror32(TD[byte32(s0, 0)], 24) ^ *key++;\n\n    for (size_t i = 0; i < 4; i++)\n    {\n        s0 = TD[byte32(t0, 3)] ^ ror32(TD[byte32(t3, 2)], 8) ^ ror32(TD[byte32(t2, 1)], 16) ^ ror32(TD[byte32(t1, 0)], 24) ^ *key++;\n        s1 = TD[byte32(t1, 3)] ^ ror32(TD[byte32(t0, 2)], 8) ^ ror32(TD[byte32(t3, 1)], 16) ^ ror32(TD[byte32(t2, 0)], 24) ^ *key++;\n        s2 = TD[byte32(t2, 3)] ^ ror32(TD[byte32(t1, 2)], 8) ^ ror32(TD[byte32(t0, 1)], 16) ^ ror32(TD[byte32(t3, 0)], 24) ^ *key++;\n        s3 = TD[byte32(t3, 3)] ^ ror32(TD[byte32(t2, 2)], 8) ^ ror32(TD[byte32(t1, 1)], 16) ^ ror32(TD[byte32(t0, 0)], 24) ^ *key++;\n\n        t0 = TD[byte32(s0, 3)] ^ ror32(TD[byte32(s3, 2)], 8) ^ ror32(TD[byte32(s2, 1)], 16) ^ ror32(TD[byte32(s1, 0)], 24) ^ *key++;\n        t1 = TD[byte32(s1, 3)] ^ ror32(TD[byte32(s0, 2)], 8) ^ ror32(TD[byte32(s3, 1)], 16) ^ ror32(TD[byte32(s2, 0)], 24) ^ *key++;\n        t2 = TD[byte32(s2, 3)] ^ ror32(TD[byte32(s1, 2)], 8) ^ ror32(TD[byte32(s0, 1)], 16) ^ ror32(TD[byte32(s3, 0)], 24) ^ *key++;\n        t3 = TD[byte32(s3, 3)] ^ ror32(TD[byte32(s2, 2)], 8) ^ ror32(TD[byte32(s1, 1)], 16) ^ ror32(TD[byte32(s0, 0)], 24) ^ *key++;\n    }\n\n    s0 = (Td[byte32(t0, 3)] << 24) ^ (Td[byte32(t3, 2)] << 16) ^ (Td[byte32(t2, 1)] << 8) ^ Td[byte32(t1, 0)] ^ *key++;\n    s1 = (Td[byte32(t1, 3)] << 24) ^ (Td[byte32(t0, 2)] << 16) ^ (Td[byte32(t3, 1)] << 8) ^ Td[byte32(t2, 0)] ^ *key++;\n    s2 = (Td[byte32(t2, 3)] << 24) ^ (Td[byte32(t1, 2)] << 16) ^ (Td[byte32(t0, 1)] << 8) ^ Td[byte32(t3, 0)] ^ *key++;\n    s3 = (Td[byte32(t3, 3)] << 24) ^ (Td[byte32(t2, 2)] << 16) ^ (Td[byte32(t1, 1)] << 8) ^ Td[byte32(t0, 0)] ^ *key++;\n\n    set32be(output + 0, s0);\n    set32be(output + 4, s1);\n    set32be(output + 8, s2);\n    set32be(output + 12, s3);\n}\n\nvoid aes128_ecb_encrypt(const aes128_key* ctx, const uint8_t* input, uint8_t* output)\n{\n#if PLATFORM_SUPPORTS_AESNI\n    if (aes128_supported_x86())\n    {\n        aes128_ecb_encrypt_x86(ctx, input, output);\n        return;\n    }\n#endif\n    aes128_encrypt(ctx, input, output);\n}\n\nvoid aes128_ecb_decrypt(const aes128_key* ctx, const uint8_t* input, uint8_t* output)\n{\n#if PLATFORM_SUPPORTS_AESNI\n    if (aes128_supported_x86())\n    {\n        aes128_ecb_decrypt_x86(ctx, input, output);\n        return;\n    }\n#endif\n    aes128_decrypt(ctx, input, output);\n}\n\nstatic void ctr_add(uint8_t* counter, uint64_t n)\n{\n    for (int i=15; i>=0; i--)\n    {\n        n = n + counter[i];\n        counter[i] = (uint8_t)n;\n        n >>= 8;\n    }\n}\n\nvoid aes128_ctr_xor(const aes128_key* context, const uint8_t* iv, uint64_t block, uint8_t* buffer, size_t size)\n{\n    uint8_t tmp[16];\n    uint8_t counter[16];\n    for (uint32_t i=0; i<16; i++)\n    {\n        counter[i] = iv[i];\n    }\n    ctr_add(counter, block);\n\n#if PLATFORM_SUPPORTS_AESNI\n    if (aes128_supported_x86())\n    {\n        aes128_ctr_xor_x86(context, counter, buffer, size);\n        return;\n    }\n#endif\n\n    while (size >= 16)\n    {\n        aes128_encrypt(context, counter, tmp);\n        for (uint32_t i=0; i<16; i++)\n        {\n            *buffer++ ^= tmp[i];\n        }\n        ctr_add(counter, 1);\n        size -= 16;\n    }\n\n    if (size != 0)\n    {\n        aes128_encrypt(context, counter, tmp);\n        for (size_t i=0; i<size; i++)\n        {\n            *buffer++ ^= tmp[i];\n        }\n    }\n}\n\n// https://tools.ietf.org/rfc/rfc4493.txt\n\ntypedef struct {\n    aes128_key key;\n    uint8_t last[16];\n    uint8_t block[16];\n    uint32_t size;\n} aes128_cmac_ctx;\n\nstatic void aes128_cmac_process(const aes128_key* ctx, uint8_t* block, const uint8_t *buffer, uint32_t size)\n{\n    assert(size % 16 == 0);\n\n#if PLATFORM_SUPPORTS_AESNI\n    if (aes128_supported_x86())\n    {\n        aes128_cmac_process_x86(ctx, block, buffer, size);\n        return;\n    }\n#endif\n    for (uint32_t i = 0; i < size; i += 16)\n    {\n        for (size_t k = 0; k < 16; k++)\n        {\n            block[k] ^= *buffer++;\n        }\n        aes128_ecb_encrypt(ctx, block, block);\n    }\n}\n\nstatic void aes128_cmac_init(aes128_cmac_ctx* ctx, const uint8_t* key)\n{\n    aes128_init(&ctx->key, key);\n    memset(ctx->last, 0, 16);\n    ctx->size = 0;\n}\n\nstatic void aes128_cmac_update(aes128_cmac_ctx* ctx, const uint8_t* buffer, uint32_t size)\n{\n    if (ctx->size + size <= 16)\n    {\n        memcpy(ctx->block + ctx->size, buffer, size);\n        ctx->size += size;\n        return;\n    }\n\n    if (ctx->size != 0)\n    {\n        uint32_t avail = 16 - ctx->size;\n        memcpy(ctx->block + ctx->size, buffer, avail < size ? avail : size);\n        buffer += avail;\n        size -= avail;\n\n        aes128_cmac_process(&ctx->key, ctx->last, ctx->block, 16);\n    }\n\n    if (size >= 16)\n    {\n        uint32_t full = (size - 1) & ~15;\n        aes128_cmac_process(&ctx->key, ctx->last, buffer, full);\n        buffer += full;\n        size -= full;\n    }\n\n    memcpy(ctx->block, buffer, size);\n    ctx->size = size;\n}\n\nstatic void cmac_gfmul(uint8_t* block)\n{\n    uint8_t carry = 0;\n    for (int i = 15; i >= 0; i--)\n    {\n        uint8_t x = block[i];\n        block[i] = (block[i] << 1) | (carry >> 7);\n        carry = x;\n    }\n\n    block[15] ^= (carry & 0x80 ? 0x87 : 0);\n}\n\nstatic void aes128_cmac_done(aes128_cmac_ctx* ctx, uint8_t* mac)\n{\n    uint8_t zero[16] = { 0 };\n    aes128_ecb_encrypt(&ctx->key, zero, mac);\n\n    cmac_gfmul(mac);\n\n    if (ctx->size != 16)\n    {\n        cmac_gfmul(mac);\n\n        ctx->block[ctx->size] = 0x80;\n        memset(ctx->block + ctx->size + 1, 0, 16 - (ctx->size + 1));\n    }\n\n    for (size_t i = 0; i < 16; i++)\n    {\n        mac[i] ^= ctx->block[i];\n    }\n\n    aes128_cmac_process(&ctx->key, mac, ctx->last, 16);\n}\n\nvoid aes128_cmac(const uint8_t* key, const uint8_t* buffer, uint32_t size, uint8_t* mac)\n{\n    aes128_cmac_ctx ctx;\n    aes128_cmac_init(&ctx, key);\n    aes128_cmac_update(&ctx, buffer, size);\n    aes128_cmac_done(&ctx, mac);\n}\n\nvoid aes128_psp_decrypt(const aes128_key* ctx, const uint8_t* iv, uint32_t index, uint8_t* buffer, uint32_t size)\n{\n    assert(size % 16 == 0);\n\n    uint8_t PKG_ALIGN(16) prev[16];\n    uint8_t PKG_ALIGN(16) block[16];\n\n    if (index == 0)\n    {\n        memset(prev, 0, 16);\n    }\n    else\n    {\n        memcpy(prev, iv, 12);\n        set32le(prev + 12, index);\n    }\n\n    memcpy(block, iv, 16);\n    set32le(block + 12, index);\n\n#if PLATFORM_SUPPORTS_AESNI\n    if (aes128_supported_x86())\n    {\n        aes128_psp_decrypt_x86(ctx, prev, block, buffer, size);\n        return;\n    }\n#endif\n\n    for (uint32_t i = 0; i < size; i += 16)\n    {\n        set32le(block + 12, get32le(block + 12) + 1);\n\n        uint8_t out[16];\n        aes128_ecb_decrypt(ctx, block, out);\n\n        for (size_t k = 0; k < 16; k++)\n        {\n            *buffer++ ^= prev[k] ^ out[k];\n        }\n        memcpy(prev, block, 16);\n    }\n}\n"
  },
  {
    "path": "pkg2zip_aes.h",
    "content": "#pragma once\n\n#include \"pkg2zip_utils.h\"\n\ntypedef struct aes128_key {\n    uint32_t PKG_ALIGN(16) key[44];\n} aes128_key;\n\nvoid aes128_init(aes128_key* ctx, const uint8_t* key);\nvoid aes128_init_dec(aes128_key* ctx, const uint8_t* key);\n\nvoid aes128_ecb_encrypt(const aes128_key* ctx, const uint8_t* input, uint8_t* output);\nvoid aes128_ecb_decrypt(const aes128_key* ctx, const uint8_t* input, uint8_t* output);\n\nvoid aes128_ctr_xor(const aes128_key* ctx, const uint8_t* iv, uint64_t block, uint8_t* buffer, size_t size);\n\nvoid aes128_cmac(const uint8_t* key, const uint8_t* buffer, uint32_t size, uint8_t* mac);\n\nvoid aes128_psp_decrypt(const aes128_key* ctx, const uint8_t* iv, uint32_t index, uint8_t* buffer, uint32_t size);\n"
  },
  {
    "path": "pkg2zip_aes_x86.c",
    "content": "#include \"pkg2zip_aes.h\"\n\n#include <string.h>\n#include <wmmintrin.h> // AESNI\n#include <tmmintrin.h> // SSSE3\n\n#define AES128_INIT(ctx, x, rcon)           \\\n{                                           \\\n    __m128i a, b;                           \\\n    _mm_store_si128(ctx, x);                \\\n    a = _mm_aeskeygenassist_si128(x, rcon); \\\n    a = _mm_shuffle_epi32(a, 0xff);         \\\n    b = _mm_slli_si128(x, 4);               \\\n    x = _mm_xor_si128(x, b);                \\\n    b = _mm_slli_si128(b, 4);               \\\n    x = _mm_xor_si128(x, b);                \\\n    b = _mm_slli_si128(b, 4);               \\\n    x = _mm_xor_si128(x, b);                \\\n    x = _mm_xor_si128(x, a);                \\\n}\n\nvoid aes128_init_x86(aes128_key* ctx, const uint8_t* key)\n{\n    __m128i* ekey = (__m128i*)ctx->key;\n\n    __m128i x = _mm_loadu_si128((const __m128i*)key);\n    AES128_INIT(ekey + 0, x, 0x01);\n    AES128_INIT(ekey + 1, x, 0x02);\n    AES128_INIT(ekey + 2, x, 0x04);\n    AES128_INIT(ekey + 3, x, 0x08);\n    AES128_INIT(ekey + 4, x, 0x10);\n    AES128_INIT(ekey + 5, x, 0x20);\n    AES128_INIT(ekey + 6, x, 0x40);\n    AES128_INIT(ekey + 7, x, 0x80);\n    AES128_INIT(ekey + 8, x, 0x1b);\n    AES128_INIT(ekey + 9, x, 0x36);\n    _mm_store_si128(ekey + 10, x);\n}\n\nvoid aes128_init_dec_x86(aes128_key* ctx, const uint8_t* key)\n{\n    aes128_key enc;\n    aes128_init_x86(&enc, key);\n\n    const __m128i* ekey = (__m128i*)&enc.key;\n    __m128i* dkey = (__m128i*)&ctx->key;\n\n    _mm_store_si128(dkey + 10, _mm_load_si128(ekey + 0));\n    for (size_t i = 1; i < 10; i++)\n    {\n        _mm_store_si128(dkey + 10 - i, _mm_aesimc_si128(_mm_load_si128(ekey + i)));\n    }\n    _mm_store_si128(dkey + 0, _mm_load_si128(ekey + 10));\n}\n\nstatic __m128i aes128_encrypt_x86(__m128i input, const __m128i* key)\n{\n    __m128i tmp = _mm_xor_si128(input, _mm_load_si128(key + 0));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 1));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 2));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 3));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 4));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 5));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 6));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 7));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 8));\n    tmp = _mm_aesenc_si128(tmp, _mm_load_si128(key + 9));\n    return _mm_aesenclast_si128(tmp, _mm_load_si128(key + 10));\n}\n\nstatic __m128i aes128_decrypt_x86(__m128i input, const __m128i* key)\n{\n    __m128i tmp = _mm_xor_si128(input, _mm_load_si128(key + 0));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 1));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 2));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 3));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 4));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 5));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 6));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 7));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 8));\n    tmp = _mm_aesdec_si128(tmp, _mm_load_si128(key + 9));\n    return _mm_aesdeclast_si128(tmp, _mm_load_si128(key + 10));\n}\n\nvoid aes128_ecb_encrypt_x86(const aes128_key* ctx, const uint8_t* input, uint8_t* output)\n{\n    const __m128i* key = (__m128i*)ctx->key;\n    __m128i tmp = aes128_encrypt_x86(_mm_loadu_si128((const __m128i*)input), key);\n    _mm_storeu_si128((__m128i*)output, tmp);\n}\n\nvoid aes128_ecb_decrypt_x86(const aes128_key* ctx, const uint8_t* input, uint8_t* output)\n{\n    const __m128i* key = (__m128i*)ctx->key;\n    __m128i tmp = aes128_decrypt_x86(_mm_loadu_si128((const __m128i*)input), key);\n    _mm_storeu_si128((__m128i*)output, tmp);\n}\n\nstatic __m128i ctr_increment(__m128i counter)\n{\n    __m128i swap = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);\n    __m128i tmp = _mm_shuffle_epi8(counter, swap);\n    tmp = _mm_add_epi64(tmp, _mm_set_epi32(0, 0, 0, 1));\n    return _mm_shuffle_epi8(tmp, swap);\n}\n\nvoid aes128_ctr_xor_x86(const aes128_key* ctx, const uint8_t* iv, uint8_t* buffer, size_t size)\n{\n    const __m128i* key = (__m128i*)ctx->key;\n    __m128i counter = _mm_loadu_si128((const __m128i*)iv);\n\n    while (size >= 16)\n    {\n        __m128i block = aes128_encrypt_x86(counter, key);\n        __m128i tmp = _mm_xor_si128(_mm_loadu_si128((const __m128i*)buffer), block);\n        _mm_storeu_si128((__m128i*)buffer, tmp);\n\n        counter = ctr_increment(counter);\n\n        buffer += 16;\n        size -= 16;\n    }\n\n    if (size != 0)\n    {\n        uint8_t full[16];\n        memcpy(full, buffer, size);\n        memset(full + size, 0, 16 - size);\n\n        __m128i block = aes128_encrypt_x86(counter, key);\n        __m128i tmp = _mm_xor_si128(_mm_loadu_si128((const __m128i*)full), block);\n        _mm_storeu_si128((__m128i*)full, tmp);\n\n        memcpy(buffer, full, size);\n    }\n}\n\nvoid aes128_cmac_process_x86(const aes128_key* ctx, uint8_t* block, const uint8_t* buffer, uint32_t size)\n{\n    const __m128i* key = (__m128i*)ctx->key;\n    __m128i* data = (__m128i*)buffer;\n\n    __m128i tmp = _mm_loadu_si128((__m128i*)block);\n    for (uint32_t i = 0; i < size; i += 16)\n    {\n        __m128i input = _mm_loadu_si128(data++);\n        tmp = _mm_xor_si128(tmp, input);\n        tmp = aes128_encrypt_x86(tmp, key);\n    }\n    _mm_storeu_si128((__m128i*)block, tmp);\n}\n\nvoid aes128_psp_decrypt_x86(const aes128_key* ctx, const uint8_t* prev, const uint8_t* block, uint8_t* buffer, uint32_t size)\n{\n    const __m128i* key = (__m128i*)ctx->key;\n    __m128i one = _mm_setr_epi32(0, 0, 0, 1);\n\n    __m128i x = _mm_load_si128((__m128i*)prev);\n    __m128i y = _mm_load_si128((__m128i*)block);\n\n    __m128i* data = (__m128i*)buffer;\n\n    for (uint32_t i = 0; i < size; i += 16)\n    {\n        y = _mm_add_epi32(y, one);\n\n        __m128i out = aes128_decrypt_x86(y, key);\n\n        out = _mm_xor_si128(out, _mm_loadu_si128(data));\n        out = _mm_xor_si128(out, x);\n        _mm_storeu_si128(data++, out);\n        x = y;\n    }\n}\n"
  },
  {
    "path": "pkg2zip_crc32.c",
    "content": "#include \"pkg2zip_crc32.h\"\n#include \"pkg2zip_utils.h\"\n\n#ifdef __linux__\n#include <endian.h>\n#endif\n\n#if defined(_MSC_VER)\n#define PLATFORM_SUPPORTS_PCLMUL 1\n\n#include <intrin.h>\nstatic void get_cpuid(uint32_t level, uint32_t* arr)\n{\n    __cpuidex((int*)arr, level, 0);\n}\n\n#elif defined(__x86_64__) || defined(__i386__)\n#define PLATFORM_SUPPORTS_PCLMUL 1\n\n#include <cpuid.h>\nstatic void get_cpuid(uint32_t level, uint32_t* arr)\n{\n    __cpuid_count(level, 0, arr[0], arr[1], arr[2], arr[3]);\n}\n\n#else\n#define PLATFORM_SUPPORTS_PCLMUL 0\n#endif\n\n#if PLATFORM_SUPPORTS_PCLMUL\nstatic int crc32_supported_x86()\n{\n    static int init = 0;\n    static int supported;\n    if (!init)\n    {\n        init = 1;\n\n        uint32_t a[4];\n        get_cpuid(0, a);\n\n        if (a[0] >= 1)\n        {\n            get_cpuid(1, a);\n            supported = ((a[2] & (1 << 9)) && (a[2] & (1 << 1)) && (a[2] & (1 << 19)));\n        }\n    }\n    return supported;\n}\n\nvoid crc32_init_x86(crc32_ctx* ctx);\nvoid crc32_update_x86(crc32_ctx* ctx, const void* buffer, size_t size);\nuint32_t crc32_done_x86(crc32_ctx* ctx);\n\n#endif\n\nstatic const uint32_t crc32[4][256] =\n{\n    {\n        0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,\n        0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,\n        0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n        0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,\n        0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,\n        0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n        0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,\n        0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,\n        0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n        0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,\n        0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,\n        0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n        0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,\n        0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,\n        0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n        0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,\n        0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,\n        0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n        0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,\n        0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,\n        0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n        0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,\n        0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,\n        0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n        0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,\n        0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,\n        0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n        0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,\n        0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,\n        0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n        0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,\n        0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,\n    },\n    {\n        0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7,\n        0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf,\n        0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496,\n        0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e,\n        0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265,\n        0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d,\n        0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034,\n        0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c,\n        0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2,\n        0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca,\n        0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93,\n        0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b,\n        0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60,\n        0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768,\n        0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31,\n        0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539,\n        0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c,\n        0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484,\n        0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd,\n        0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5,\n        0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e,\n        0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026,\n        0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f,\n        0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773, 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277,\n        0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189,\n        0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81,\n        0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8,\n        0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0,\n        0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b,\n        0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23,\n        0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a,\n        0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72,\n    },\n    {\n        0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685,\n        0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d,\n        0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5,\n        0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d,\n        0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065,\n        0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd,\n        0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315,\n        0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad,\n        0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45,\n        0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd,\n        0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835,\n        0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d,\n        0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5,\n        0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d,\n        0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5,\n        0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d,\n        0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05,\n        0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd,\n        0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75,\n        0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd,\n        0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5,\n        0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d,\n        0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895,\n        0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d,\n        0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5,\n        0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d,\n        0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5,\n        0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d,\n        0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625,\n        0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d,\n        0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555,\n        0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed,\n    },\n    {\n        0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9,\n        0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056,\n        0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26,\n        0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9,\n        0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787,\n        0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68,\n        0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018,\n        0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7,\n        0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084,\n        0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b,\n        0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b,\n        0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4,\n        0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba,\n        0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755,\n        0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825,\n        0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca,\n        0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82,\n        0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d,\n        0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d,\n        0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2,\n        0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc,\n        0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953,\n        0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623,\n        0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b, 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc,\n        0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf,\n        0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50,\n        0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120,\n        0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf,\n        0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981,\n        0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e,\n        0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e,\n        0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1,\n    }\n};\n\nvoid crc32_init(crc32_ctx* ctx)\n{\n#if PLATFORM_SUPPORTS_PCLMUL\n    if (crc32_supported_x86())\n    {\n        crc32_init_x86(ctx);\n        return;\n    }\n#endif\n    ctx->crc[0] = 0xffffffff;\n}\n\nvoid crc32_update(crc32_ctx* ctx, const void* buffer, size_t size)\n{\n#if PLATFORM_SUPPORTS_PCLMUL\n    if (crc32_supported_x86())\n    {\n        crc32_update_x86(ctx, buffer, size);\n        return;\n    }\n#endif\n\n    const uint8_t* buffer8 = buffer;\n    uint32_t value = ctx->crc[0];\n    while (size >= 4)\n    {\n#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN\n        uint32_t x = get32be(buffer8);\n        value = __builtin_bswap32(value) ^ x;\n        value = crc32[0][value & 0xff] ^\n                crc32[1][(value >> 8) & 0xff] ^\n                crc32[2][(value >> 16) & 0xff] ^\n                crc32[3][(value >> 24) & 0xff];\n#else\n        uint32_t x = get32le(buffer8);\n        value = value ^ x;\n        value = crc32[0][(value >> 24) & 0xff] ^\n                crc32[1][(value >> 16) & 0xff] ^\n                crc32[2][(value >> 8) & 0xff] ^\n                crc32[3][value & 0xff];\n#endif\n        buffer8 += sizeof(x);\n        size -= sizeof(x);\n    }\n\n    while (size --> 0)\n    {\n        value = (value >> 8) ^ crc32[0][(uint8_t)value ^ *buffer8++];\n    }\n\n    ctx->crc[0] = value;\n}\n\nuint32_t crc32_done(crc32_ctx* ctx)\n{\n#if PLATFORM_SUPPORTS_PCLMUL\n    if (crc32_supported_x86())\n    {\n        return crc32_done_x86(ctx);\n    }\n#endif\n\n    return ~ctx->crc[0];\n}\n\nstatic uint32_t crc32_gfmulv(const uint32_t* m, uint32_t v)\n{\n    uint32_t r = 0;\n    while (v)\n    {\n        if (v & 1)\n        {\n            r ^= *m;\n        }\n        v >>= 1;\n        m++;\n    }\n    return r;\n}\n\nstatic void crc32_gfsq(const uint32_t* m, uint32_t* r)\n{\n    for (size_t i = 0; i < 32; i++)\n    {\n        r[i] = crc32_gfmulv(m, m[i]);\n    }\n}\n\nuint32_t crc32_combine(uint32_t a, uint32_t b, uint32_t blen)\n{\n    uint32_t m1[32];\n    uint32_t m2[32];\n\n    m1[0] = 0xedb88320;\n    for (uint32_t i = 1; i < 32; i++)\n    {\n        m1[i] = 1 << (i - 1);\n    }\n\n    crc32_gfsq(m1, m2);\n    crc32_gfsq(m2, m1);\n\n    for (;;)\n    {\n        crc32_gfsq(m1, m2);\n        if (blen & 1)\n        {\n            a = crc32_gfmulv(m2, a);\n        }\n        blen >>= 1;\n\n        if (blen == 0)\n        {\n            break;\n        }\n\n        crc32_gfsq(m2, m1);\n        if (blen & 1)\n        {\n            a = crc32_gfmulv(m1, a);\n        }\n        blen >>= 1;\n\n        if (blen == 0)\n        {\n            break;\n        }\n    }\n\n    return a ^ b;\n}\n"
  },
  {
    "path": "pkg2zip_crc32.h",
    "content": "#pragma once\n\n#include \"pkg2zip_utils.h\"\n\ntypedef struct {\n    uint32_t PKG_ALIGN(16) crc[4 * 5];\n} crc32_ctx;\n\nvoid crc32_init(crc32_ctx* ctx);\nvoid crc32_update(crc32_ctx* ctx, const void* buffer, size_t size);\nuint32_t crc32_done(crc32_ctx* ctx);\n\n// returns crc32(x||y)\n// where a=crc32(x) and b=crc32(y)\n// crc32(x||y) = crc32(x||z) ^ crc32(y), where z=00...00 (same length as y)\nuint32_t crc32_combine(uint32_t a, uint32_t b, uint32_t blen);\n"
  },
  {
    "path": "pkg2zip_crc32_x86.c",
    "content": "#include \"pkg2zip_crc32.h\"\n\n#include <wmmintrin.h> // PCLMUL\n#include <tmmintrin.h> // SSSE3\n#include <smmintrin.h> // SSS4\n\n// Whitepaper: https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf\n// ZLIB licensed code from https://github.com/jtkukunas/zlib/blob/master/crc_folding.c\n\nstatic const uint32_t PKG_ALIGN(16) shift_table[] = {\n    0x84838281, 0x88878685, 0x8c8b8a89, 0x008f8e8d,\n    0x85848382, 0x89888786, 0x8d8c8b8a, 0x01008f8e,\n    0x86858483, 0x8a898887, 0x8e8d8c8b, 0x0201008f,\n    0x87868584, 0x8b8a8988, 0x8f8e8d8c, 0x03020100,\n    0x88878685, 0x8c8b8a89, 0x008f8e8d, 0x04030201,\n    0x89888786, 0x8d8c8b8a, 0x01008f8e, 0x05040302,\n    0x8a898887, 0x8e8d8c8b, 0x0201008f, 0x06050403,\n    0x8b8a8988, 0x8f8e8d8c, 0x03020100, 0x07060504,\n    0x8c8b8a89, 0x008f8e8d, 0x04030201, 0x08070605,\n    0x8d8c8b8a, 0x01008f8e, 0x05040302, 0x09080706,\n    0x8e8d8c8b, 0x0201008f, 0x06050403, 0x0a090807,\n    0x8f8e8d8c, 0x03020100, 0x07060504, 0x0b0a0908,\n    0x008f8e8d, 0x04030201, 0x08070605, 0x0c0b0a09,\n    0x01008f8e, 0x05040302, 0x09080706, 0x0d0c0b0a,\n    0x0201008f, 0x06050403, 0x0a090807, 0x0e0d0c0b,\n};\n\n#define FOLD1(xmm0, xmm1, xmm2, xmm3) do         \\\n{                                                \\\n    const __m128i fold4 = _mm_set_epi32(         \\\n        0x00000001, 0x54442bd4,                  \\\n        0x00000001, 0xc6e41596);                 \\\n                                                 \\\n    __m128i r0, r1, r2, r3, a, b;                \\\n                                                 \\\n    r0 = xmm1;                                   \\\n    r1 = xmm2;                                   \\\n    r2 = xmm3;                                   \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm0, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm0, fold4, 0x10); \\\n    r3 = _mm_xor_si128(a, b);                    \\\n                                                 \\\n    xmm0 = r0;                                   \\\n    xmm1 = r1;                                   \\\n    xmm2 = r2;                                   \\\n    xmm3 = r3;                                   \\\n} while (0)\n\n#define FOLD2(xmm0, xmm1, xmm2, xmm3) do         \\\n{                                                \\\n    const __m128i fold4 = _mm_set_epi32(         \\\n        0x00000001, 0x54442bd4,                  \\\n        0x00000001, 0xc6e41596);                 \\\n                                                 \\\n    __m128i r0, r1, r2, r3, a, b;                \\\n                                                 \\\n    r0 = xmm2;                                   \\\n    r1 = xmm3;                                   \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm0, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm0, fold4, 0x10); \\\n    r2 = _mm_xor_si128(a, b);                    \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm1, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm1, fold4, 0x10); \\\n    r3 = _mm_xor_si128(a, b);                    \\\n                                                 \\\n    xmm0 = r0;                                   \\\n    xmm1 = r1;                                   \\\n    xmm2 = r2;                                   \\\n    xmm3 = r3;                                   \\\n} while (0)\n\n#define FOLD3(xmm0, xmm1, xmm2, xmm3) do         \\\n{                                                \\\n    const __m128i fold4 = _mm_set_epi32(         \\\n        0x00000001, 0x54442bd4,                  \\\n        0x00000001, 0xc6e41596);                 \\\n                                                 \\\n    __m128i r0, r1, r2, r3, a, b;                \\\n                                                 \\\n    r0 = xmm3;                                   \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm0, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm0, fold4, 0x10); \\\n    r1 = _mm_xor_si128(a, b);                    \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm1, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm1, fold4, 0x10); \\\n    r2 = _mm_xor_si128(a, b);                    \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm2, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm2, fold4, 0x10); \\\n    r3 = _mm_xor_si128(a, b);                    \\\n                                                 \\\n    xmm0 = r0;                                   \\\n    xmm1 = r1;                                   \\\n    xmm2 = r2;                                   \\\n    xmm3 = r3;                                   \\\n} while (0)\n\n#define FOLD4(xmm0, xmm1, xmm2, xmm3) do         \\\n{                                                \\\n    const __m128i fold4 = _mm_set_epi32(         \\\n        0x00000001, 0x54442bd4,                  \\\n        0x00000001, 0xc6e41596);                 \\\n                                                 \\\n    __m128i a, b;                                \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm0, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm0, fold4, 0x10); \\\n    xmm0 = _mm_xor_si128(a, b);                  \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm1, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm1, fold4, 0x10); \\\n    xmm1 = _mm_xor_si128(a, b);                  \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm2, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm2, fold4, 0x10); \\\n    xmm2 = _mm_xor_si128(a, b);                  \\\n                                                 \\\n    a = _mm_clmulepi64_si128(xmm3, fold4, 0x01); \\\n    b = _mm_clmulepi64_si128(xmm3, fold4, 0x10); \\\n    xmm3 = _mm_xor_si128(a, b);                  \\\n} while (0)\n\n#define PARTIAL(len, xmm0, xmm1, xmm2, xmm3, xmm4) do                 \\\n{                                                                     \\\n    const __m128i fold4 = _mm_set_epi32(                              \\\n        0x00000001, 0x54442bd4,                                       \\\n        0x00000001, 0xc6e41596);                                      \\\n    const __m128i mask = _mm_set1_epi32(0x80808080);                  \\\n                                                                      \\\n    __m128i shl = _mm_load_si128((__m128i *)shift_table + (len - 1)); \\\n    __m128i shr = _mm_xor_si128(shl, mask);                           \\\n                                                                      \\\n    __m128i a, b, r;                                                  \\\n    __m128i tmp = _mm_shuffle_epi8(xmm0, shl);                        \\\n                                                                      \\\n    a = _mm_shuffle_epi8(xmm0, shr);                                  \\\n    b = _mm_shuffle_epi8(xmm1, shl);                                  \\\n    xmm0 = _mm_or_si128(a, b);                                        \\\n                                                                      \\\n    a = _mm_shuffle_epi8(xmm1, shr);                                  \\\n    b = _mm_shuffle_epi8(xmm2, shl);                                  \\\n    xmm1 = _mm_or_si128(a, b);                                        \\\n                                                                      \\\n    a = _mm_shuffle_epi8(xmm2, shr);                                  \\\n    b = _mm_shuffle_epi8(xmm3, shl);                                  \\\n    xmm2 = _mm_or_si128(a, b);                                        \\\n                                                                      \\\n    a = _mm_shuffle_epi8(xmm3, shr);                                  \\\n    b = _mm_shuffle_epi8(xmm4, shl);                                  \\\n    xmm4 = b;                                                         \\\n    r = _mm_or_si128(a, b);                                           \\\n                                                                      \\\n    a = _mm_clmulepi64_si128(tmp, fold4, 0x10);                       \\\n    b = _mm_clmulepi64_si128(tmp, fold4, 0x01);                       \\\n                                                                      \\\n    r = _mm_xor_si128(r, a);                                          \\\n    r = _mm_xor_si128(r, b);                                          \\\n    xmm3 = r;                                                         \\\n} while(0)\n\nvoid crc32_init_x86(crc32_ctx* ctx)\n{\n    __m128i init = _mm_cvtsi32_si128(0x9db42487);\n    __m128i zero = _mm_setzero_si128();\n\n    _mm_store_si128((__m128i*)ctx->crc + 0, init);\n    _mm_store_si128((__m128i*)ctx->crc + 1, zero);\n    _mm_store_si128((__m128i*)ctx->crc + 2, zero);\n    _mm_store_si128((__m128i*)ctx->crc + 3, zero);\n}\n\nvoid crc32_update_x86(crc32_ctx* ctx, const void* buffer, size_t size)\n{\n    const uint8_t* buffer8 = buffer;\n\n    __m128i xmm0 = _mm_load_si128((__m128i*)ctx->crc + 0);\n    __m128i xmm1 = _mm_load_si128((__m128i*)ctx->crc + 1);\n    __m128i xmm2 = _mm_load_si128((__m128i*)ctx->crc + 2);\n    __m128i xmm3 = _mm_load_si128((__m128i*)ctx->crc + 3);\n    __m128i xmm4 = _mm_load_si128((__m128i*)ctx->crc + 4);\n\n    if (size < 16)\n    {\n        if (size == 0)\n        {\n            return;\n        }\n        xmm4 = _mm_loadu_si128((__m128i *)buffer8);\n        goto partial;\n    }\n\n    uint32_t prefix = (0 - (uintptr_t)buffer8) & 0xF;\n    if (prefix != 0)\n    {\n        xmm4 = _mm_loadu_si128((__m128i *)buffer8);\n        buffer8 += prefix;\n        size -= prefix;\n\n        PARTIAL(prefix, xmm0, xmm1, xmm2, xmm3, xmm4);\n    }\n\n    while (size >= 64)\n    {\n        __m128i t0 = _mm_load_si128((__m128i *)buffer8 + 0);\n        __m128i t1 = _mm_load_si128((__m128i *)buffer8 + 1);\n        __m128i t2 = _mm_load_si128((__m128i *)buffer8 + 2);\n        __m128i t3 = _mm_load_si128((__m128i *)buffer8 + 3);\n\n        FOLD4(xmm0, xmm1, xmm2, xmm3);\n\n        xmm0 = _mm_xor_si128(xmm0, t0);\n        xmm1 = _mm_xor_si128(xmm1, t1);\n        xmm2 = _mm_xor_si128(xmm2, t2);\n        xmm3 = _mm_xor_si128(xmm3, t3);\n\n        buffer8 += 64;\n        size -= 64;\n    }\n\n    if (size >= 48)\n    {\n        __m128i t0 = _mm_load_si128((__m128i *)buffer8 + 0);\n        __m128i t1 = _mm_load_si128((__m128i *)buffer8 + 1);\n        __m128i t2 = _mm_load_si128((__m128i *)buffer8 + 2);\n\n        FOLD3(xmm0, xmm1, xmm2, xmm3);\n\n        xmm1 = _mm_xor_si128(xmm1, t0);\n        xmm2 = _mm_xor_si128(xmm2, t1);\n        xmm3 = _mm_xor_si128(xmm3, t2);\n\n        buffer8 += 48;\n        size -= 48;\n    }\n    else if (size >= 32)\n    {\n        __m128i t0 = _mm_load_si128((__m128i *)buffer8 + 0);\n        __m128i t1 = _mm_load_si128((__m128i *)buffer8 + 1);\n\n        FOLD2(xmm0, xmm1, xmm2, xmm3);\n\n        xmm2 = _mm_xor_si128(xmm2, t0);\n        xmm3 = _mm_xor_si128(xmm3, t1);\n\n        buffer8 += 32;\n        size -= 32;\n    }\n    else if (size >= 16)\n    {\n        __m128i t0 = _mm_load_si128((__m128i *)buffer8 + 0);\n\n        FOLD1(xmm0, xmm1, xmm2, xmm3);\n\n        xmm3 = _mm_xor_si128(xmm3, t0);\n\n        buffer8 += 16;\n        size -= 16;\n    }\n\n    if (size == 0)\n    {\n        goto done;\n    }\n\n    xmm4 = _mm_load_si128((__m128i *)buffer8);\n\npartial:\n    PARTIAL(size, xmm0, xmm1, xmm2, xmm3, xmm4);\n\ndone:\n    _mm_store_si128((__m128i*)ctx->crc + 0, xmm0);\n    _mm_store_si128((__m128i*)ctx->crc + 1, xmm1);\n    _mm_store_si128((__m128i*)ctx->crc + 2, xmm2);\n    _mm_store_si128((__m128i*)ctx->crc + 3, xmm3);\n    _mm_store_si128((__m128i*)ctx->crc + 4, xmm4);\n}\n\nuint32_t crc32_done_x86(crc32_ctx* ctx)\n{\n    const __m128i mask1 = _mm_setr_epi32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000);\n    const __m128i mask2 = _mm_setr_epi32(0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);\n\n    __m128i xmm0 = _mm_load_si128((__m128i*)ctx->crc + 0);\n    __m128i xmm1 = _mm_load_si128((__m128i*)ctx->crc + 1);\n    __m128i xmm2 = _mm_load_si128((__m128i*)ctx->crc + 2);\n    __m128i xmm3 = _mm_load_si128((__m128i*)ctx->crc + 3);\n\n    __m128i fold;\n    __m128i a, b, t;\n\n    fold = _mm_setr_epi32(0xccaa009e, 0x00000000, 0x751997d0, 0x00000001);\n\n    a = _mm_clmulepi64_si128(xmm0, fold, 0x10);\n    b = _mm_clmulepi64_si128(xmm0, fold, 0x01);\n    t = _mm_xor_si128(xmm1, a);\n    t = _mm_xor_si128(t, b);\n\n    a = _mm_clmulepi64_si128(t, fold, 0x10);\n    b = _mm_clmulepi64_si128(t, fold, 0x01);\n    t = _mm_xor_si128(xmm2, a);\n    t = _mm_xor_si128(t, b);\n\n    a = _mm_clmulepi64_si128(t, fold, 0x10);\n    b = _mm_clmulepi64_si128(t, fold, 0x01);\n    t = _mm_xor_si128(xmm3, a);\n    t = _mm_xor_si128(t, b);\n\n    fold = _mm_setr_epi32(0xccaa009e, 0x00000000, 0x63cd6124, 0x00000001);\n\n    a = _mm_clmulepi64_si128(t, fold, 0);\n    b = _mm_srli_si128(t, 8);\n    a = _mm_xor_si128(a, b);\n\n    b = _mm_slli_si128(a, 4);\n    b = _mm_clmulepi64_si128(b, fold, 0x10);\n    t = _mm_xor_si128(a, b);\n    t = _mm_and_si128(t, mask2);\n\n    fold = _mm_setr_epi32(0xf7011640, 0x00000001, 0xdb710640, 0x00000001);\n\n    a = _mm_clmulepi64_si128(t, fold, 0);\n    a = _mm_xor_si128(a, t);\n    a = _mm_and_si128(a, mask1);\n\n    b = _mm_clmulepi64_si128(a, fold, 0x10);\n    b = _mm_xor_si128(b, a);\n    b = _mm_xor_si128(b, t);\n\n    uint32_t crc = _mm_extract_epi32(b, 2);\n    return ~crc;\n}\n"
  },
  {
    "path": "pkg2zip_out.c",
    "content": "#include \"pkg2zip_out.h\"\n#include \"pkg2zip_sys.h\"\n#include \"pkg2zip_zip.h\"\n#include <stdio.h>\n\nstatic zip out_zip;\nstatic int out_zipped;\nstatic sys_file out_file;\nstatic uint64_t out_file_offset;\n\nvoid out_begin(const char* name, int zipped)\n{\n    if (zipped)\n    {\n        zip_create(&out_zip, name);\n    }\n    out_zipped = zipped;\n}\n\nvoid out_end(void)\n{\n    if (out_zipped)\n    {\n        zip_close(&out_zip);\n    }\n}\n\nvoid out_add_folder(const char* path)\n{\n    if (out_zipped)\n    {\n        zip_add_folder(&out_zip, path);\n    }\n    else\n    {\n        sys_mkdir(path);\n    }\n}\n\nvoid out_add_parent(const char* path)\n{\n    char parent[1024];\n    char* lastslash = strrchr(path, '/');\n    if (lastslash != NULL)\n    {\n        snprintf(parent, strlen(path)-strlen(lastslash)+1, \"%s\", path);\n        if (out_zipped)\n        {\n            zip_add_folder(&out_zip, parent);\n        }\n        else\n        {\n            sys_mkdir(parent);\n        }\n    }\n}\n\nuint64_t out_begin_file(const char* name, int compress)\n{\n    if (out_zipped)\n    {\n        return zip_begin_file(&out_zip, name, compress);\n    }\n    else\n    {\n        out_file = sys_create(name);\n        out_file_offset = 0;\n        return 0;\n    }\n}\n\nvoid out_end_file(void)\n{\n    if (out_zipped)\n    {\n        zip_end_file(&out_zip);\n    }\n    else\n    {\n        sys_close(out_file);\n    }\n}\n\nvoid out_write(const void* buffer, uint32_t size)\n{\n    if (out_zipped)\n    {\n        zip_write_file(&out_zip, buffer, size);\n    }\n    else\n    {\n        sys_write(out_file, out_file_offset, buffer, size);\n        out_file_offset += size;\n    }\n}\n\nvoid out_write_at(uint64_t offset, const void* buffer, uint32_t size)\n{\n    if (out_zipped)\n    {\n        zip_write_file_at(&out_zip, offset, buffer, size);\n    }\n    else\n    {\n        sys_write(out_file, offset, buffer, size);\n    }\n}\n\nvoid out_set_offset(uint64_t offset)\n{\n    if (out_zipped)\n    {\n        zip_set_offset(&out_zip, offset);\n    }\n    else\n    {\n        out_file_offset = offset;\n\n    }\n}\n\nuint32_t out_zip_get_crc32(void)\n{\n    if (out_zipped)\n    {\n        return zip_get_crc32(&out_zip);\n    }\n    return 0;\n}\n\nvoid out_zip_set_crc32(uint32_t crc)\n{\n    if (out_zipped)\n    {\n        zip_set_crc32(&out_zip, crc);\n    }\n}\n"
  },
  {
    "path": "pkg2zip_out.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\nvoid out_begin(const char* name, int zipped);\nvoid out_end(void);\nvoid out_add_folder(const char* path);\nvoid out_add_parent(const char* path);\nuint64_t out_begin_file(const char* name, int compress);\nvoid out_end_file(void);\nvoid out_write(const void* buffer, uint32_t size);\n\n// hacky solution to be able to write cso header after the data is written\nvoid out_write_at(uint64_t offset, const void* buffer, uint32_t size);\nvoid out_set_offset(uint64_t offset);\nuint32_t out_zip_get_crc32(void);\nvoid out_zip_set_crc32(uint32_t crc);\n"
  },
  {
    "path": "pkg2zip_psp.c",
    "content": "#include \"pkg2zip_psp.h\"\n#include \"pkg2zip_out.h\"\n#include \"pkg2zip_crc32.h\"\n#include \"pkg2zip_utils.h\"\n#include \"miniz_tdef.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#define ISO_SECTOR_SIZE 2048\n\n#define CSO_HEADER_SIZE 24\n\n// https://vitadevwiki.com/vita/Keys_NonVita#PSPAESKirk4.2F7\nstatic const uint8_t kirk7_key38[] = { 0x12, 0x46, 0x8d, 0x7e, 0x1c, 0x42, 0x20, 0x9b, 0xba, 0x54, 0x26, 0x83, 0x5e, 0xb0, 0x33, 0x03 };\nstatic const uint8_t kirk7_key39[] = { 0xc4, 0x3b, 0xb6, 0xd6, 0x53, 0xee, 0x67, 0x49, 0x3e, 0xa9, 0x5f, 0xbc, 0x0c, 0xed, 0x6f, 0x8a };\nstatic const uint8_t kirk7_key63[] = { 0x9c, 0x9b, 0x13, 0x72, 0xf8, 0xc6, 0x40, 0xcf, 0x1c, 0x62, 0xf5, 0xd5, 0x92, 0xdd, 0xb5, 0x82 };\n\n// https://vitadevwiki.com/vita/Keys_NonVita#PSPAMHashKey\nstatic const uint8_t amctl_hashkey_3[] = { 0xe3, 0x50, 0xed, 0x1d, 0x91, 0x0a, 0x1f, 0xd0, 0x29, 0xbb, 0x1c, 0x3e, 0xf3, 0x40, 0x77, 0xfb };\nstatic const uint8_t amctl_hashkey_4[] = { 0x13, 0x5f, 0xa4, 0x7c, 0xab, 0x39, 0x5b, 0xa4, 0x76, 0xb8, 0xcc, 0xa9, 0x8f, 0x3a, 0x04, 0x45 };\nstatic const uint8_t amctl_hashkey_5[] = { 0x67, 0x8d, 0x7f, 0xa3, 0x2a, 0x9c, 0xa0, 0xd1, 0x50, 0x8a, 0xd8, 0x38, 0x5e, 0x4b, 0x01, 0x7e };\n\n// lzrc decompression code from libkirk by tpu\ntypedef struct {\n    // input stream\n    const uint8_t* input;\n    uint32_t in_ptr;\n    uint32_t in_len;\n\n    // output stream\n    uint8_t* output;\n    uint32_t out_ptr;\n    uint32_t out_len;\n\n    // range decode\n    uint32_t range;\n    uint32_t code;\n    uint32_t out_code;\n    uint8_t lc;\n\n    uint8_t bm_literal[8][256];\n    uint8_t bm_dist_bits[8][39];\n    uint8_t bm_dist[18][8];\n    uint8_t bm_match[8][8];\n    uint8_t bm_len[8][31];\n} lzrc_decode;\n\nstatic void rc_init(lzrc_decode* rc, void* out, int out_len, const void* in, int in_len)\n{\n    if (in_len < 5)\n    {\n        sys_error(\"ERROR: internal error - lzrc input underflow! pkg may be corrupted?\\n\");\n    }\n\n    rc->input = in;\n    rc->in_len = in_len;\n    rc->in_ptr = 5;\n\n    rc->output = out;\n    rc->out_len = out_len;\n    rc->out_ptr = 0;\n\n    rc->range = 0xffffffff;\n    rc->lc = rc->input[0];\n    rc->code = get32be(rc->input + 1);\n    rc->out_code = 0xffffffff;\n\n    memset(rc->bm_literal, 0x80, sizeof(rc->bm_literal));\n    memset(rc->bm_dist_bits, 0x80, sizeof(rc->bm_dist_bits));\n    memset(rc->bm_dist, 0x80, sizeof(rc->bm_dist));\n    memset(rc->bm_match, 0x80, sizeof(rc->bm_match));\n    memset(rc->bm_len, 0x80, sizeof(rc->bm_len));\n}\n\nstatic void normalize(lzrc_decode* rc)\n{\n    if (rc->range < 0x01000000)\n    {\n        rc->range <<= 8;\n        rc->code = (rc->code << 8) + rc->input[rc->in_ptr];\n        rc->in_ptr++;\n    }\n}\n\nstatic int rc_bit(lzrc_decode* rc, uint8_t *prob)\n{\n    uint32_t bound;\n\n    normalize(rc);\n\n    bound = (rc->range >> 8) * (*prob);\n    *prob -= *prob >> 3;\n\n    if (rc->code < bound)\n    {\n        rc->range = bound;\n        *prob += 31;\n        return 1;\n    }\n    else\n    {\n        rc->code -= bound;\n        rc->range -= bound;\n        return 0;\n    }\n}\n\nstatic int rc_bittree(lzrc_decode* rc, uint8_t *probs, int limit)\n{\n    int number = 1;\n\n    do\n    {\n        number = (number << 1) + rc_bit(rc, probs + number);\n    }\n    while (number < limit);\n\n    return number;\n}\n\nstatic int rc_number(lzrc_decode* rc, uint8_t *prob, uint32_t n)\n{\n    int number = 1;\n\n    if (n > 3)\n    {\n        number = (number << 1) + rc_bit(rc, prob + 3);\n        if (n > 4)\n        {\n            number = (number << 1) + rc_bit(rc, prob + 3);\n            if (n > 5)\n            {\n                // direct bits\n                normalize(rc);\n\n                for (uint32_t i = 0; i < n - 5; i++)\n                {\n                    rc->range >>= 1;\n                    number <<= 1;\n                    if (rc->code < rc->range)\n                    {\n                        number += 1;\n                    }\n                    else\n                    {\n                        rc->code -= rc->range;\n                    }\n                }\n            }\n        }\n    }\n\n    if (n > 0)\n    {\n        number = (number << 1) + rc_bit(rc, prob);\n        if (n > 1)\n        {\n            number = (number << 1) + rc_bit(rc, prob + 1);\n            if (n > 2)\n            {\n                number = (number << 1) + rc_bit(rc, prob + 2);\n            }\n        }\n    }\n\n    return number;\n}\n\nstatic int lzrc_decompress(void* out, int out_len, const void* in, int in_len)\n{\n    lzrc_decode rc;\n    rc_init(&rc, out, out_len, in, in_len);\n\n    if (rc.lc & 0x80)\n    {\n        // plain text\n        memcpy(rc.output, rc.input + 5, rc.code);\n        return rc.code;\n    }\n\n    int rc_state = 0;\n    uint8_t last_byte = 0;\n\n    for (;;)\n    {\n        uint32_t match_step = 0;\n\n        int bit = rc_bit(&rc, &rc.bm_match[rc_state][match_step]);\n        if (bit == 0) // literal\n        {\n            if (rc_state > 0)\n            {\n                rc_state -= 1;\n            }\n\n            int byte = rc_bittree(&rc, &rc.bm_literal[((last_byte >> rc.lc) & 0x07)][0], 0x100);\n            byte -= 0x100;\n\n            if (rc.out_ptr == rc.out_len)\n            {\n                sys_error(\"ERROR: internal error - lzrc output overflow! pkg may be corrupted?\\n\");\n            }\n            rc.output[rc.out_ptr++] = (uint8_t)byte;\n            last_byte = (uint8_t)byte;\n        }\n        else // match\n        {\n            // find bits of match length\n            uint32_t len_bits = 0;\n            for (int i = 0; i < 7; i++)\n            {\n                match_step += 1;\n                bit = rc_bit(&rc, &rc.bm_match[rc_state][match_step]);\n                if (bit == 0)\n                {\n                    break;\n                }\n                len_bits += 1;\n            }\n\n            // find match length\n            uint32_t match_len;\n            if (len_bits == 0)\n            {\n                match_len = 1;\n            }\n            else\n            {\n                uint32_t len_state = ((len_bits - 1) << 2) + ((rc.out_ptr << (len_bits - 1)) & 0x03);\n                match_len = rc_number(&rc, &rc.bm_len[rc_state][len_state], len_bits);\n                if (match_len == 0xFF)\n                {\n                    // end of stream\n                    return rc.out_ptr;\n                }\n            }\n\n            // find number of bits of match distance\n            uint32_t dist_state = 0;\n            uint32_t limit = 8;\n            if (match_len > 2)\n            {\n                dist_state += 7;\n                limit = 44;\n            }\n            int dist_bits = rc_bittree(&rc, &rc.bm_dist_bits[len_bits][dist_state], limit);\n            dist_bits -= limit;\n\n            // find match distance\n            uint32_t match_dist;\n            if (dist_bits > 0)\n            {\n                match_dist = rc_number(&rc, &rc.bm_dist[dist_bits][0], dist_bits);\n            }\n            else\n            {\n                match_dist = 1;\n            }\n\n            // copy match bytes\n            if (match_dist > rc.out_ptr)\n            {\n                sys_error(\"ERROR: internal error - lzrc match_dist out of range! pkg may be corrupted?\\n\");\n            }\n\n            if (rc.out_ptr + match_len + 1 > rc.out_len)\n            {\n                sys_error(\"ERROR: internal error - lzrc output overflow! pkg may be corrupted?\\n\");\n            }\n\n            const uint8_t* match_src = rc.output + rc.out_ptr - match_dist;\n            for (uint32_t i = 0; i <= match_len; i++)\n            {\n                rc.output[rc.out_ptr++] = *match_src++;\n            }\n            last_byte = match_src[-1];\n\n            rc_state = 6 + ((rc.out_ptr + 1) & 1);\n        }\n    }\n}\n\nstatic void init_psp_decrypt(aes128_key* key, uint8_t* iv, int eboot, const uint8_t* mac, const uint8_t* header, uint32_t offset1, uint32_t offset2)\n{\n    uint8_t tmp[16];\n    aes128_init_dec(key, kirk7_key63);\n    if (eboot)\n    {\n        aes128_ecb_decrypt(key, header + offset1, tmp);\n    }\n    else\n    {\n        memcpy(tmp, header + offset1, 16);\n    }\n\n    aes128_key aes;\n    aes128_init_dec(&aes, kirk7_key38);\n    aes128_ecb_decrypt(&aes, tmp, tmp);\n\n    for (size_t i = 0; i < 16; i++)\n    {\n        iv[i] = mac[i] ^ tmp[i] ^ header[offset2 + i] ^ amctl_hashkey_3[i] ^ amctl_hashkey_5[i];\n    }\n    aes128_init_dec(&aes, kirk7_key39);\n    aes128_ecb_decrypt(&aes, iv, iv);\n\n    for (size_t i = 0; i < 16; i++)\n    {\n        iv[i] ^= amctl_hashkey_4[i];\n    }\n}\n\nstatic void init_psx_keys(uint8_t* iv, const uint8_t* mac, const uint8_t* header, uint32_t offset1)\n{\n    uint8_t tmp[16];\n    memcpy(tmp, header + offset1, 16);\n\n    aes128_key aes;\n    aes128_init_dec(&aes, kirk7_key38);\n    aes128_ecb_decrypt(&aes, tmp, tmp);\n\n    for (size_t i = 0; i < 16; i++)\n    {\n        iv[i] = mac[i] ^ tmp[i] ^ amctl_hashkey_3[i];\n    }\n\n}\n\n\nvoid unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size, int cso)\n{\n    if (item_size < 0x28)\n    {\n        sys_error(\"ERROR: eboot.pbp file is to short!\\n\");\n    }\n\n    uint8_t eboot_header[0x28];\n    sys_read(pkg, enc_offset + item_offset, eboot_header, sizeof(eboot_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, item_offset / 16, eboot_header, sizeof(eboot_header));\n\n    if (memcmp(eboot_header, \"\\x00PBP\", 4) != 0)\n    {\n        sys_error(\"ERROR: wrong eboot.pbp header signature!\\n\");\n    }\n\n    uint32_t psar_offset = get32le(eboot_header + 0x24);\n    if (psar_offset + 256 > item_size)\n    {\n        sys_error(\"ERROR: eboot.pbp file is to short!\\n\");\n    }\n    assert(psar_offset % 16 == 0);\n\n    uint8_t psar_header[256];\n    sys_read(pkg, enc_offset + item_offset + psar_offset, psar_header, sizeof(psar_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + psar_offset) / 16, psar_header, sizeof(psar_header));\n\n    if (memcmp(psar_header, \"NPUMDIMG\", 8) != 0)\n    {\n        sys_error(\"ERROR: wrong data.psar header signature!\\n\");\n    }\n\n    uint32_t iso_block = get32le(psar_header + 0x0c);\n    if (iso_block > 16)\n    {\n        sys_error(\"ERROR: unsupported data.psar block size %u, max %u supported!\\b\", iso_block, 16);\n    }\n\n    uint8_t mac[16];\n    aes128_cmac(kirk7_key38, psar_header, 0xc0, mac);\n\n    aes128_key psp_key;\n    uint8_t psp_iv[16];\n    init_psp_decrypt(&psp_key, psp_iv, 1, mac, psar_header, 0xc0, 0xa0);\n    aes128_psp_decrypt(&psp_key, psp_iv, 0, psar_header + 0x40, 0x60);\n\n    uint32_t iso_start = get32le(psar_header + 0x54);\n    uint32_t iso_end = get32le(psar_header + 0x64);\n    uint32_t iso_total = iso_end - iso_start - 1;\n    uint32_t block_count = (iso_total + iso_block - 1) / iso_block;\n\n    uint32_t iso_table = get32le(psar_header + 0x6c);\n\n    if (iso_table + block_count * 32 > item_size)\n    {\n        sys_error(\"ERROR: offset table in data.psar file is too large!\\n\");\n    }\n\n    mz_uint cso_compress_flags = 0;\n    uint32_t cso_index = 0;\n    uint32_t cso_offset = 0;\n    uint64_t cso_size = 0;\n    uint32_t* cso_block = NULL;\n    uint32_t initial_size = 0;\n\n    uint64_t file_offset = out_begin_file(path, !cso);\n    if (cso)\n    {\n        cso_size = block_count * iso_block * ISO_SECTOR_SIZE;\n        cso_compress_flags = tdefl_create_comp_flags_from_zip_params(cso, -MZ_DEFAULT_WINDOW_BITS, MZ_FIXED);\n\n        uint32_t cso_block_count = (uint32_t)(1 + (cso_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE);\n        cso_block = sys_realloc(NULL, cso_block_count * sizeof(uint32_t));\n\n        initial_size = CSO_HEADER_SIZE + cso_block_count * sizeof(uint32_t);\n        out_set_offset(file_offset + initial_size);\n\n        cso_offset = initial_size;\n    }\n\n    for (uint32_t i = 0; i < block_count; i++)\n    {\n        uint64_t table_offset = item_offset + psar_offset + iso_table + 32 * i;\n\n        uint8_t table[32];\n        sys_read(pkg, enc_offset + table_offset, table, sizeof(table));\n        aes128_ctr_xor(pkg_key, pkg_iv, table_offset / 16, table, sizeof(table));\n\n        uint32_t t[8];\n        for (size_t k = 0; k < 8; k++)\n        {\n            t[k] = get32le(table + k * 4);\n        }\n\n        uint32_t block_offset = t[4] ^ t[2] ^ t[3];\n        uint32_t block_size = t[5] ^ t[1] ^ t[2];\n        uint32_t block_flags = t[6] ^ t[0] ^ t[3];\n\n        if (psar_offset + block_size > item_size)\n        {\n            sys_error(\"ERROR: iso block size/offset is to large!\\n\");\n        }\n\n        uint8_t PKG_ALIGN(16) data[16 * ISO_SECTOR_SIZE];\n\n        uint64_t abs_offset = item_offset + psar_offset + block_offset;\n        sys_output_progress(enc_offset + abs_offset);\n        sys_read(pkg, enc_offset + abs_offset, data, block_size);\n        aes128_ctr_xor(pkg_key, pkg_iv, abs_offset / 16, data, block_size);\n\n        if ((block_flags & 4) == 0)\n        {\n            aes128_psp_decrypt(&psp_key, psp_iv, block_offset / 16, data, block_size);\n        }\n\n        uint32_t out_size;\n        if (block_size == iso_block * ISO_SECTOR_SIZE)\n        {\n            if (cso)\n            {\n                for (size_t n = 0; n < iso_block * ISO_SECTOR_SIZE; n += ISO_SECTOR_SIZE)\n                {\n                    cso_block[cso_index] = cso_offset;\n\n                    uint8_t PKG_ALIGN(16) output[ISO_SECTOR_SIZE];\n                    size_t insize = ISO_SECTOR_SIZE;\n                    size_t outsize = sizeof(output);\n\n                    tdefl_compressor c;\n                    tdefl_init(&c, cso_compress_flags);\n                    tdefl_status st = tdefl_compress(&c, data + n, &insize, output, &outsize, TDEFL_FINISH);\n                    if (st == TDEFL_STATUS_DONE)\n                    {\n                        out_write(output, (uint32_t)outsize);\n                        cso_offset += (uint32_t)outsize;\n                    }\n                    else\n                    {\n                        cso_block[cso_index] |= 0x80000000;\n                        out_write(data + n, ISO_SECTOR_SIZE);\n                        cso_offset += ISO_SECTOR_SIZE;\n                    }\n                    cso_index++;\n                }\n            }\n            else\n            {\n                out_write(data, (uint32_t)block_size);\n            }\n        }\n        else\n        {\n            uint8_t PKG_ALIGN(16) uncompressed[16 * ISO_SECTOR_SIZE];\n            out_size = lzrc_decompress(uncompressed, sizeof(uncompressed), data, block_size);\n            if (out_size != iso_block * ISO_SECTOR_SIZE)\n            {\n                sys_error(\"ERROR: internal error - lzrc decompression failed! pkg may be corrupted?\\n\");\n            }\n            if (cso)\n            {\n                for (size_t n = 0; n < iso_block * ISO_SECTOR_SIZE; n += ISO_SECTOR_SIZE)\n                {\n                    cso_block[cso_index] = cso_offset;\n\n                    uint8_t output[ISO_SECTOR_SIZE];\n                    size_t insize = ISO_SECTOR_SIZE;\n                    size_t outsize = sizeof(output);\n\n                    tdefl_compressor c;\n                    tdefl_init(&c, cso_compress_flags);\n                    tdefl_status st = tdefl_compress(&c, uncompressed + n, &insize, output, &outsize, TDEFL_FINISH);\n                    if (st == TDEFL_STATUS_DONE)\n                    {\n                        out_write(output, (uint32_t)outsize);\n                        cso_offset += (uint32_t)outsize;\n                    }\n                    else\n                    {\n                        cso_block[cso_index] |= 0x80000000;\n                        out_write(uncompressed + n, ISO_SECTOR_SIZE);\n                        cso_offset += ISO_SECTOR_SIZE;\n                    }\n                    cso_index++;\n                }\n            }\n            else\n            {\n                out_write(uncompressed, (uint32_t)out_size);\n            }\n        }\n    }\n\n    if (cso)\n    {\n        cso_block[cso_index++] = cso_offset;\n\n        uint8_t cso_header[CSO_HEADER_SIZE] = { 0x43, 0x49, 0x53, 0x4f };\n        // header size\n        set32le(cso_header + 4, sizeof(cso_header));\n        // original size\n        set64le(cso_header + 8, cso_size);\n        // block size\n        set32le(cso_header + 16, ISO_SECTOR_SIZE);\n        // version\n        cso_header[20] = 1;\n\n        out_write_at(file_offset, cso_header, sizeof(cso_header));\n        out_write_at(file_offset + sizeof(cso_header), cso_block, cso_index * sizeof(uint32_t));\n\n        crc32_ctx cheader;\n        crc32_init(&cheader);\n        crc32_update(&cheader, cso_header, sizeof(cso_header));\n        crc32_update(&cheader, cso_block, cso_index * sizeof(uint32_t));\n\n        uint32_t header_crc32 = crc32_done(&cheader);\n        uint32_t data_crc32 = out_zip_get_crc32();\n        uint32_t data_len = (uint32_t)(cso_offset - initial_size);\n\n        uint32_t crc32 = crc32_combine(header_crc32, data_crc32, data_len);\n        out_zip_set_crc32(crc32);\n\n        sys_realloc(cso_block, 0);\n    }\n\n    out_end_file();\n}\n\nvoid unpack_psp_key(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size)\n{\n    if (item_size < 0x90 + 0xa0)\n    {\n        sys_error(\"ERROR: PSP-KEY.EDAT file is to short!\\n\");\n    }\n\n    uint8_t key_header[0xa0];\n    sys_read(pkg, enc_offset + item_offset + 0x90, key_header, sizeof(key_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + 0x90) / 16, key_header, sizeof(key_header));\n\n    if (memcmp(key_header, \"\\x00PGD\", 4) != 0)\n    {\n        sys_error(\"ERROR: wrong PSP-KEY.EDAT header signature!\\n\");\n    }\n\n    uint32_t key_index = get32le(key_header + 4);\n    uint32_t drm_type = get32le(key_header + 8);\n    if (key_index != 1 || drm_type != 1)\n    {\n        sys_error(\"ERROR: unsupported PSP-KEY.EDAT file, key/drm type is wrong!\\n\");\n    }\n\n    uint8_t mac[16];\n    aes128_cmac(kirk7_key38, key_header, 0x70, mac);\n\n    aes128_key psp_key;\n    uint8_t psp_iv[16];\n    init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x10);\n    aes128_psp_decrypt(&psp_key, psp_iv, 0, key_header + 0x30, 0x30);\n\n    uint32_t data_size = get32le(key_header + 0x44);\n    uint32_t data_offset = get32le(key_header + 0x4c);\n\n    if (data_size != 0x10 || data_offset != 0x90)\n    {\n        sys_error(\"ERROR: unsupported PSP-KEY.EDAT file, data/offset is wrong!\\n\");\n    }\n\n    init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x30);\n    aes128_psp_decrypt(&psp_key, psp_iv, 0, key_header + 0x90, 0x10);\n\n    out_begin_file(path, 0);\n    out_write(key_header + 0x90, 0x10);\n    out_end_file();\n}\n\nvoid unpack_psp_edat(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size)\n{\n    if (item_size < 0x90 + 0xa0)\n    {\n        sys_error(\"ERROR: EDAT file is to short!\\n\");\n    }\n\n    uint8_t item_header[90];\n    sys_read(pkg, enc_offset + item_offset, item_header, sizeof(item_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset) / 16, item_header, sizeof(item_header));\n    uint8_t key_header_offset = item_header[0xC];\n\n    uint8_t key_header[0xa0];\n    sys_read(pkg, enc_offset + item_offset + key_header_offset, key_header, sizeof(key_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + key_header_offset) / 16, key_header, sizeof(key_header));\n\n    if (memcmp(key_header, \"\\x00PGD\", 4) != 0)\n    {\n        sys_error(\"ERROR: wrong EDAT header signature!\\n\");\n    }\n\n    uint32_t key_index = get32le(key_header + 4);\n    uint32_t drm_type = get32le(key_header + 8);\n\n    if (key_index != 1 || drm_type != 1)\n    {\n        sys_error(\"ERROR: unsupported EDAT file, key/drm type is wrong!\\n\");\n    }\n\n    uint8_t mac[16];\n    aes128_cmac(kirk7_key38, key_header, 0x70, mac);\n\n    aes128_key psp_key;\n    uint8_t psp_iv[16];\n    init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x10);\n    aes128_psp_decrypt(&psp_key, psp_iv, 0, key_header + 0x30, 0x30);\n\n    uint32_t data_size = get32le(key_header + 0x44);\n    uint32_t data_offset = get32le(key_header + 0x4c);\n\n    if (data_offset != 0x90)\n    {\n        sys_error(\"ERROR: unsupported EDAT file, data offset is wrong!\\n\");\n    }\n\n    init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x30);\n\n    uint32_t block_size = 0x10;\n    uint32_t block_count = ((data_size + (block_size - 1)) / block_size );\n\n    out_begin_file(path, 0);\n    for (uint32_t i = 0; i < block_count; i++)\n    {\n        uint8_t block[0x10];\n        uint32_t block_offset = (data_offset + (i * block_size)); \n\n        sys_read(pkg, enc_offset + item_offset + key_header_offset + block_offset, block, block_size);\n        aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + key_header_offset + block_offset)  / 16, block, block_size);\n        aes128_psp_decrypt(&psp_key, psp_iv, i * block_size / 16, block, block_size);\n\n        uint32_t out_size = 0x10;\n        if ( ((i + 1) * block_size) > data_size )\n        {\n            out_size = data_size - (i * block_size);\n        }\n        out_write(block, out_size);\n    }\n    out_end_file();\n}\n\nvoid unpack_keys_bin(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size)\n{\n\n    if (item_size < 0x28 + 0x10 + 0x1f0)\n    {\n        sys_error(\"ERROR: EBOOT file is to short!\\n\");\n    }\n\n    uint8_t eboot_header[0x28];\n    sys_read(pkg, enc_offset + item_offset, eboot_header, sizeof(eboot_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, item_offset / 16, eboot_header, sizeof(eboot_header));\n\n    if (memcmp(eboot_header, \"\\x00PBP\", 4) != 0)\n    {\n        sys_error(\"ERROR: wrong eboot header signature!\\n\");\n    }\n\n    uint32_t psar_offset = get32le(eboot_header + 0x24);\n\n    if (psar_offset + 16 > item_size)\n    {\n        sys_error(\"ERROR: eboot file is to short!\\n\");\n    }\n    assert(psar_offset % 16 == 0);\n\n    uint8_t psar_header[16];\n    sys_read(pkg, enc_offset + item_offset + psar_offset, psar_header, sizeof(psar_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + psar_offset) / 16, psar_header, sizeof(psar_header));\n\n    uint32_t key_header_offset = 0;\n    if (memcmp(psar_header, \"PSTITLE\", 7) == 0)\n    {\n        key_header_offset = 0x1f0;\n    }\n    else if (memcmp(psar_header, \"PSISO\", 5) == 0)\n    {\n        key_header_offset = 0x3f0;\n    }\n    else \n    {\n        sys_error(\"ERROR: wrong psar header signature!\\n\");\n    }\n\n    uint8_t key_header[0x90];\n    sys_read(pkg, enc_offset + item_offset + psar_offset+ 16 + key_header_offset, key_header, sizeof(key_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + psar_offset + 16 + key_header_offset) / 16, key_header, sizeof(key_header));\n\n    if (memcmp(key_header, \"\\x00PGD\", 4) != 0)\n    {\n        sys_error(\"ERROR: wrong key header signature!\\n\");\n    }\n\n    uint32_t key_index = get32le(key_header + 4);\n    uint32_t drm_type = get32le(key_header + 8);\n\n    if (key_index != 1 || drm_type != 1)\n    {\n        sys_error(\"ERROR: unsupported EBOOT file, key/drm type is unsupported!\\n\");\n    }\n\n    uint8_t mac[16];\n    aes128_cmac(kirk7_key38, key_header, 0x70, mac);\n\n    uint8_t keys_bin[16];\n    init_psx_keys(keys_bin, mac, key_header, 0x70);\n\n    out_begin_file(path, 0);\n    out_write(keys_bin, sizeof(keys_bin));\n    out_end_file();\n}\n\nvoid get_psp_theme_title(char* title, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset)\n{\n    uint8_t item_header[90];\n    sys_read(pkg, enc_offset + item_offset, item_header, sizeof(item_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset) / 16, item_header, sizeof(item_header));\n    uint8_t key_header_offset = item_header[0xC];\n\n    uint8_t key_header[0xa0];\n    sys_read(pkg, enc_offset + item_offset + key_header_offset, key_header, sizeof(key_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + key_header_offset) / 16, key_header, sizeof(key_header));\n\n    if (memcmp(key_header, \"\\x00PGD\", 4) != 0)\n    {\n        sys_error(\"ERROR: wrong EDAT header signature!\\n\");\n    }\n\n    uint32_t key_index = get32le(key_header + 4);\n    uint32_t drm_type = get32le(key_header + 8);\n\n    if (key_index != 1 || drm_type != 1)\n    {\n        sys_error(\"ERROR: unsupported EDAT file, key/drm type is wrong!\\n\");\n    }\n\n    uint8_t mac[16];\n    aes128_cmac(kirk7_key38, key_header, 0x70, mac);\n\n    aes128_key psp_key;\n    uint8_t psp_iv[16];\n    init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x10);\n    aes128_psp_decrypt(&psp_key, psp_iv, 0, key_header + 0x30, 0x30);\n\n    uint32_t data_offset = get32le(key_header + 0x4c);\n\n    uint8_t theme_header[256];\n    sys_read(pkg, enc_offset + item_offset + key_header_offset + data_offset, theme_header, sizeof(theme_header));\n    aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + key_header_offset + data_offset)/ 16, theme_header, sizeof(theme_header));\n\n    init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x30);\n    aes128_psp_decrypt(&psp_key, psp_iv, 0, theme_header, sizeof(theme_header));\n\n    if (memcmp(theme_header, \"\\x00PTF\", 4) != 0)\n    {\n        sys_error(\"ERROR: wrong PTF header signature!\\n\");\n    }\n\n    memcpy(title,theme_header+8, 128);\n    title[128]=0;\n}\n"
  },
  {
    "path": "pkg2zip_psp.h",
    "content": "#include \"pkg2zip_aes.h\"\n#include \"pkg2zip_sys.h\"\n\nvoid unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size, int cso);\nvoid unpack_psp_key(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size);\nvoid unpack_psp_edat(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size);\nvoid unpack_keys_bin(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size);\nvoid get_psp_theme_title(char* title, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset);\n"
  },
  {
    "path": "pkg2zip_sys.c",
    "content": "#include \"pkg2zip_sys.h\"\n#include \"pkg2zip_utils.h\"\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdarg.h>\n#include <sys/stat.h>\n#include <errno.h>\n\n#if defined(_WIN32)\n\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n\nstatic HANDLE gStdout;\nstatic int gStdoutRedirected;\nstatic UINT gOldCP;\n\nvoid sys_output_init(void)\n{\n    gOldCP = GetConsoleOutputCP();\n    SetConsoleOutputCP(CP_UTF8);\n    gStdout = GetStdHandle(STD_OUTPUT_HANDLE);\n\n    DWORD mode;\n    gStdoutRedirected = !GetConsoleMode(gStdout, &mode);\n}\n\nvoid sys_output_done(void)\n{\n    SetConsoleOutputCP(gOldCP);\n}\n\nvoid sys_output(const char* msg, ...)\n{\n    char buffer[1024];\n\n    va_list arg;\n    va_start(arg, msg);\n    vsnprintf(buffer, sizeof(buffer), msg, arg);\n    va_end(arg);\n\n    if (!gStdoutRedirected)\n    {\n        WCHAR wbuffer[sizeof(buffer)];\n        int wcount = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wbuffer, sizeof(buffer));\n\n        DWORD written;\n        WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), wbuffer, wcount - 1, &written, NULL);\n        return;\n    }\n    fputs(buffer, stdout);\n}\n\nvoid sys_error(const char* msg, ...)\n{\n    char buffer[1024];\n\n    va_list arg;\n    va_start(arg, msg);\n    vsnprintf(buffer, sizeof(buffer), msg, arg);\n    va_end(arg);\n\n    DWORD mode;\n    if (GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &mode))\n    {\n        WCHAR wbuffer[sizeof(buffer)];\n        int wcount = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wbuffer, sizeof(buffer));\n\n        DWORD written;\n        WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), wbuffer, wcount - 1, &written, NULL);\n    }\n    else\n    {\n        fputs(buffer, stderr);\n    }\n\n    SetConsoleOutputCP(gOldCP);\n    exit(EXIT_FAILURE);\n}\n\nstatic void sys_mkdir_real(const char* path)\n{\n    WCHAR wpath[MAX_PATH];\n    MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);\n\n    if (CreateDirectoryW(wpath, NULL) == 0)\n    {\n        if (GetLastError() != ERROR_ALREADY_EXISTS)\n        {\n            sys_error(\"ERROR: cannot create '%s' folder\\n\", path);\n        }\n    }\n}\n\nsys_file sys_open(const char* fname, uint64_t* size)\n{\n    WCHAR path[MAX_PATH];\n    MultiByteToWideChar(CP_UTF8, 0, fname, -1, path, MAX_PATH);\n\n    HANDLE handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);\n    if (handle == INVALID_HANDLE_VALUE)\n    {\n        sys_error(\"ERROR: cannot open '%s' file\\n\", fname);\n    }\n\n    LARGE_INTEGER sz;\n    if (!GetFileSizeEx(handle, &sz))\n    {\n        sys_error(\"ERROR: cannot get size of '%s' file\\n\", fname);\n    }\n    *size = sz.QuadPart;\n\n    return handle;\n}\n\nsys_file sys_create(const char* fname)\n{\n    WCHAR path[MAX_PATH];\n    MultiByteToWideChar(CP_UTF8, 0, fname, -1, path, MAX_PATH);\n\n    HANDLE handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);\n    if (handle == INVALID_HANDLE_VALUE)\n    {\n        sys_error(\"ERROR:1: cannot create '%s' file\\n\", fname);\n    }\n\n    return handle;\n}\n\nvoid sys_close(sys_file file)\n{\n    if (!CloseHandle(file))\n    {\n        sys_error(\"ERROR: failed to close file\\n\");\n    }\n}\n\nvoid sys_read(sys_file file, uint64_t offset, void* buffer, uint32_t size)\n{\n    DWORD read;\n    OVERLAPPED ov;\n    ov.hEvent = NULL;\n    ov.Offset = (uint32_t)offset;\n    ov.OffsetHigh = (uint32_t)(offset >> 32);\n    if (!ReadFile(file, buffer, size, &read, &ov) || read != size)\n    {\n        sys_error(\"ERROR: failed to read %u bytes from file\\n\", size);\n    }\n}\n\nvoid sys_write(sys_file file, uint64_t offset, const void* buffer, uint32_t size)\n{\n    DWORD written;\n    OVERLAPPED ov;\n    ov.hEvent = NULL;\n    ov.Offset = (uint32_t)offset;\n    ov.OffsetHigh = (uint32_t)(offset >> 32);\n    if (!WriteFile(file, buffer, size, &written, &ov) || written != size)\n    {\n        sys_error(\"ERROR: failed to write %u bytes to file\\n\", size);\n    }\n}\n\n#else\n\n#define _FILE_OFFSET_BITS 64\n#include <stdio.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <unistd.h>\n#include <sys/stat.h>\n\nstatic int gStdoutRedirected;\n\nvoid sys_output_init(void)\n{\n    gStdoutRedirected = !isatty(STDOUT_FILENO);\n}\n\nvoid sys_output_done(void)\n{\n}\n\nvoid sys_output(const char* msg, ...)\n{\n    va_list arg;\n    va_start(arg, msg);\n    vfprintf(stdout, msg, arg);\n    va_end(arg);\n}\n\nvoid sys_error(const char* msg, ...)\n{\n    va_list arg;\n    va_start(arg, msg);\n    vfprintf(stderr, msg, arg);\n    va_end(arg);\n\n    exit(EXIT_FAILURE);\n}\n\nstatic void sys_mkdir_real(const char* path)\n{\n    if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)\n    {\n        if (errno != EEXIST)\n        {\n            sys_error(\"ERROR: cannot create '%s' folder\\n\", path);\n        }\n    }\n}\n\nsys_file sys_open(const char* fname, uint64_t* size)\n{\n    int fd = open(fname, O_RDONLY);\n    if (fd < 0)\n    {\n        sys_error(\"ERROR: cannot open '%s' file\\n\", fname);\n    }\n\n    struct stat st;\n    if (fstat(fd, &st) != 0)\n    {\n        sys_error(\"ERROR: cannot get size of '%s' file\\n\", fname);\n    }\n    *size = st.st_size;\n\n    return (void*)(intptr_t)fd;\n}\n\nsys_file sys_create(const char* fname)\n{\n    int fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);\n    if (fd < 0)\n    {\n        sys_error(\"ERROR:2: cannot create '%s' file\\n\", fname);\n    }\n\n    return (void*)(intptr_t)fd;\n}\n\nvoid sys_close(sys_file file)\n{\n    if (close((int)(intptr_t)file) != 0)\n    {\n        sys_error(\"ERROR: failed to close file\\n\");\n    }\n}\n\nvoid sys_read(sys_file file, uint64_t offset, void* buffer, uint32_t size)\n{\n    ssize_t read = pread((int)(intptr_t)file, buffer, size, offset);\n    if (read < 0 || read != (ssize_t)size)\n    {\n        sys_error(\"ERROR: failed to read %u bytes from file\\n\", size);\n    }\n}\n\nvoid sys_write(sys_file file, uint64_t offset, const void* buffer, uint32_t size)\n{\n    ssize_t wrote = pwrite((int)(intptr_t)file, buffer, size, offset);\n    if (wrote < 0 || wrote != (ssize_t)size)\n    {\n        sys_error(\"ERROR: failed to read %u bytes from file\\n\", size);\n    }\n}\n\n#endif\n\nvoid sys_mkdir(const char* path)\n{\n    char* last = strrchr(path, '/');\n    if (last)\n    {\n        *last = 0;\n        sys_mkdir(path);\n        *last = '/';\n    }\n    sys_mkdir_real(path);\n}\n\nvoid* sys_realloc(void* ptr, size_t size)\n{\n    void* result = NULL;\n    if (!ptr && size)\n    {\n        result = malloc(size);\n    }\n    else if (ptr && !size)\n    {\n        free(ptr);\n        return NULL;\n    }\n    else if (ptr && size)\n    {\n        result = realloc(ptr, size);\n    }\n    else\n    {\n        sys_error(\"ERROR: internal error, wrong sys_realloc usage\\n\");\n    }\n\n    if (!result)\n    {\n        sys_error(\"ERROR: out of memory\\n\");\n    }\n\n    return result;\n}\n\nvoid sys_vstrncat(char* dst, size_t n, const char* format, ...)\n{\n    char temp[1024];\n\n    va_list args;\n    va_start(args, format);\n    vsnprintf(temp, sizeof(temp), format, args);\n    va_end(args);\n\n    strncat(dst, temp, n - strlen(dst) - 1);\n}\n\nstatic uint64_t out_size;\nstatic uint32_t out_next;\n\nvoid sys_output_progress_init(uint64_t size)\n{\n    out_size = size;\n    out_next = 0;\n}\n\nvoid sys_output_progress(uint64_t progress)\n{\n    if (gStdoutRedirected)\n    {\n        return;\n    }\n\n    uint32_t now = (uint32_t)(progress * 100 / out_size);\n    if (now >= out_next)\n    {\n        sys_output(\"[*] unpacking... %u%%\\r\", now);\n        out_next = now + 1;\n    }\n}\n\nint sys_test_dir(const char* const path)\n{\n    struct stat info;\n\n    int statRC = stat( path, &info );\n    if( statRC != 0 )\n    {\n        if (errno == ENOENT)  { return 0; } \n        if (errno == ENOTDIR) { return 0; } \n        return -1;\n    }\n\n    return ( info.st_mode & S_IFDIR ) ? 1 : 0;\n}\n"
  },
  {
    "path": "pkg2zip_sys.h",
    "content": "#pragma once\n\n#include \"pkg2zip_utils.h\"\n\n// correctly outputs utf8 string\nvoid sys_output_init(void);\nvoid sys_output_done(void);\nvoid sys_output(const char* msg, ...);\nvoid NORETURN sys_error(const char* msg, ...);\n\nvoid sys_output_progress_init(uint64_t size);\nvoid sys_output_progress(uint64_t progress);\n\ntypedef void* sys_file;\n\nvoid sys_mkdir(const char* path);\n\nsys_file sys_open(const char* fname, uint64_t* size);\nsys_file sys_create(const char* fname);\nvoid sys_close(sys_file file);\nvoid sys_read(sys_file file, uint64_t offset, void* buffer, uint32_t size);\nvoid sys_write(sys_file file, uint64_t offset, const void* buffer, uint32_t size);\n\n// if !ptr && size => malloc\n// if ptr && !size => free\n// if ptr && size => realloc\nvoid* sys_realloc(void* ptr, size_t size);\n\nvoid sys_vstrncat(char* dst, size_t n, const char* format, ...);\n\nint sys_test_dir(const char* const path);"
  },
  {
    "path": "pkg2zip_utils.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\n#if defined(_MSC_VER)\n#  define NORETURN __declspec(noreturn)\n#  define PKG_ALIGN(x) __declspec(align(x))\n#else\n#  define NORETURN __attribute__((noreturn))\n#  define PKG_ALIGN(x) __attribute__((aligned(x)))\n#endif\n\nstatic inline uint32_t min32(uint32_t a, uint32_t b)\n{\n    return a < b ? a : b;\n}\n\nstatic inline uint64_t min64(uint64_t a, uint64_t b)\n{\n    return a < b ? a : b;\n}\n\nstatic inline uint16_t get16le(const uint8_t* bytes)\n{\n    return (bytes[0]) | (bytes[1] << 8);\n}\n\nstatic inline uint32_t get32le(const uint8_t* bytes)\n{\n    return (bytes[0]) | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);\n}\n\nstatic inline uint64_t get64le(const uint8_t* bytes)\n{\n    return (uint64_t)bytes[0]\n        | ((uint64_t)bytes[1] << 8)\n        | ((uint64_t)bytes[2] << 16)\n        | ((uint64_t)bytes[3] << 24)\n        | ((uint64_t)bytes[4] << 32)\n        | ((uint64_t)bytes[5] << 40)\n        | ((uint64_t)bytes[6] << 48)\n        | ((uint64_t)bytes[7] << 56);\n}\n\nstatic inline uint16_t get16be(const uint8_t* bytes)\n{\n    return (bytes[1]) | (bytes[0] << 8);\n}\n\nstatic inline uint32_t get32be(const uint8_t* bytes)\n{\n    return (bytes[3]) | (bytes[2] << 8) | (bytes[1] << 16) | (bytes[0] << 24);\n}\n\nstatic inline uint64_t get64be(const uint8_t* bytes)\n{\n    return (uint64_t)bytes[7]\n        | ((uint64_t)bytes[6] << 8)\n        | ((uint64_t)bytes[5] << 16)\n        | ((uint64_t)bytes[4] << 24)\n        | ((uint64_t)bytes[3] << 32)\n        | ((uint64_t)bytes[2] << 40)\n        | ((uint64_t)bytes[1] << 48)\n        | ((uint64_t)bytes[0] << 56);\n}\n\nstatic inline void set16le(uint8_t* bytes, uint16_t x)\n{\n    bytes[0] = (uint8_t)x;\n    bytes[1] = (uint8_t)(x >> 8);\n}\n\nstatic inline void set32le(uint8_t* bytes, uint32_t x)\n{\n    bytes[0] = (uint8_t)x;\n    bytes[1] = (uint8_t)(x >> 8);\n    bytes[2] = (uint8_t)(x >> 16);\n    bytes[3] = (uint8_t)(x >> 24);\n}\n\nstatic inline void set64le(uint8_t* bytes, uint64_t x)\n{\n    bytes[0] = (uint8_t)x;\n    bytes[1] = (uint8_t)(x >> 8);\n    bytes[2] = (uint8_t)(x >> 16);\n    bytes[3] = (uint8_t)(x >> 24);\n    bytes[4] = (uint8_t)(x >> 32);\n    bytes[5] = (uint8_t)(x >> 40);\n    bytes[6] = (uint8_t)(x >> 48);\n    bytes[7] = (uint8_t)(x >> 56);\n}\n\nstatic inline void set16be(uint8_t* bytes, uint16_t x)\n{\n    bytes[0] = (uint8_t)(x >> 8);\n    bytes[1] = (uint8_t)x;\n}\n\nstatic inline void set32be(uint8_t* bytes, uint32_t x)\n{\n    bytes[0] = (uint8_t)(x >> 24);\n    bytes[1] = (uint8_t)(x >> 16);\n    bytes[2] = (uint8_t)(x >> 8);\n    bytes[3] = (uint8_t)x;\n}\n\nstatic inline void set64be(uint8_t* bytes, uint64_t x)\n{\n    bytes[0] = (uint8_t)(x >> 56);\n    bytes[1] = (uint8_t)(x >> 48);\n    bytes[2] = (uint8_t)(x >> 40);\n    bytes[3] = (uint8_t)(x >> 32);\n    bytes[4] = (uint8_t)(x >> 24);\n    bytes[5] = (uint8_t)(x >> 16);\n    bytes[6] = (uint8_t)(x >> 8);\n    bytes[7] = (uint8_t)x;\n}\n"
  },
  {
    "path": "pkg2zip_zip.c",
    "content": "#if defined(__MINGW32__) && !defined(__x86_64__)\n#  define _USE_32BIT_TIME_T\n#  define __CRT__NO_INLINE\n#endif\n\n#include \"pkg2zip_zip.h\"\n#include \"pkg2zip_out.h\"\n#include \"pkg2zip_crc32.h\"\n#include \"pkg2zip_utils.h\"\n\n#include <string.h>\n#include <time.h>\n\n#define ZIP_MEMORY_BLOCK (1024 * 1024)\n\n// https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT\n\n#define ZIP_VERSION 45\n#define ZIP_METHOD_STORE 0\n#define ZIP_METHOD_DEFLATE 8\n#define ZIP_UTF8_FLAG (1 << 11)\n\n#define ZIP_DOS_ATTRIBUTE_DIRECTORY 0x10\n#define ZIP_DOS_ATTRIBUTE_ARCHIVE   0x20\n\n#define ZIP_LOCAL_HEADER_SIZE 30\n#define ZIP_GLOBAL_HEADER_SIZE 46\n#define ZIP64_EOC_DIR_SIZE 56\n#define ZIP64_EOC_DIR_LOCATOR_SIZE 20\n#define ZIP_EOC_DIR_SIZE 22\n\n#define ZIP_LOCAL_HEADER_CRC32_OFFSET 14\n#define ZIP_LOCAL_HEADER_FILENAME_LENGTH_OFFSET 26\n\nstruct zip_file\n{\n    uint64_t offset;\n    uint64_t size;\n    uint64_t compressed;\n    uint32_t crc32;\n    int compress;\n};\n\nstatic zip_file* zip_new_file(zip* z)\n{\n    if (z->count == z->max)\n    {\n        z->allocated += ZIP_MEMORY_BLOCK;\n        z->files = sys_realloc(z->files, z->allocated);\n        z->max = z->allocated / sizeof(zip_file);\n    }\n\n    return z->files + z->count++;\n}\n\nvoid zip_create(zip* z, const char* name)\n{\n    z->file = sys_create(name);\n    z->total = 0;\n    z->count = 0;\n    z->max = 0;\n    z->allocated = 0;\n    z->files = NULL;\n    z->current = NULL;\n\n    time_t t = time(NULL);\n    struct tm* tm = localtime(&t);\n    z->date = (uint16_t)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);\n    z->time = (uint16_t)((tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2));\n}\n\nvoid zip_add_folder(zip* z, const char* name)\n{\n    size_t name_length = strlen(name) + 1;\n    if (name_length > ZIP_MAX_FILENAME)\n    {\n        sys_error(\"ERROR: dirname too long\\n\");\n    }\n\n    zip_file* f = zip_new_file(z);\n    f->offset = z->total;\n    f->size = 0;\n    f->compressed = 0;\n    f->crc32 = 0;\n    f->compress = 0;\n\n    uint8_t header[ZIP_LOCAL_HEADER_SIZE] = { 0x50, 0x4b, 0x03, 0x04 };\n    // version needed to extract\n    set16le(header + 4, ZIP_VERSION);\n    // general purpose bit flag\n    set16le(header + 6, ZIP_UTF8_FLAG);\n    // compression method\n    set16le(header + 8, ZIP_METHOD_STORE);\n    // last mod file time\n    set16le(header + 10, z->time);\n    // last mod file date\n    set16le(header + 12, z->date);\n    // file name length\n    set16le(header + 26, (uint16_t)name_length);\n\n    sys_write(z->file, z->total, header, sizeof(header));\n    z->total += sizeof(header);\n\n    sys_write(z->file, z->total, name, (uint16_t)name_length);\n    z->total += name_length - 1;\n\n    char slash = '/';\n    sys_write(z->file, z->total, &slash, 1);\n    z->total += 1;\n}\n\nuint64_t zip_begin_file(zip* z, const char* name, int compress)\n{\n    size_t name_length = strlen(name);\n    if (name_length > ZIP_MAX_FILENAME)\n    {\n        sys_error(\"ERROR: filename too long\\n\");\n    }\n\n    zip_file* f = zip_new_file(z);\n    f->offset = z->total;\n    f->size = 0;\n    f->compressed = 0;\n    f->compress = compress;\n    z->current = f;\n\n    crc32_init(&z->crc32);\n    z->crc32_set = 0;\n\n    uint8_t header[ZIP_LOCAL_HEADER_SIZE] = { 0x50, 0x4b, 0x03, 0x04 };\n    // version needed to extract\n    set16le(header + 4, ZIP_VERSION);\n    // general purpose bit flag\n    set16le(header + 6, ZIP_UTF8_FLAG);\n    // compression method\n    set16le(header + 8, compress ? ZIP_METHOD_DEFLATE : ZIP_METHOD_STORE);\n    // last mod file time\n    set16le(header + 10, z->time);\n    // last mod file date\n    set16le(header + 12, z->date);\n    // file name length\n    set16le(header + 26, (uint16_t)name_length);\n\n    sys_write(z->file, z->total, header, sizeof(header));\n    z->total += sizeof(header);\n\n    sys_write(z->file, z->total, name, (uint16_t)name_length);\n    z->total += name_length;\n\n    if (compress)\n    {\n        int flags = tdefl_create_comp_flags_from_zip_params(MZ_BEST_SPEED, -MZ_DEFAULT_WINDOW_BITS, MZ_DEFAULT_STRATEGY);\n        tdefl_init(&z->tdefl, flags);\n    }\n\n    return z->total - f->offset;\n}\n\nvoid zip_write_file(zip* z, const void* data, uint32_t size)\n{\n    z->current->size += size;\n    crc32_update(&z->crc32, data, size);\n\n    if (z->current->compress)\n    {\n        const uint8_t* data8 = data;\n        while (size != 0)\n        {\n            uint8_t buffer[4096];\n\n            size_t isize = size;\n            size_t osize = sizeof(buffer);\n            tdefl_compress(&z->tdefl, data8, &isize, buffer, &osize, TDEFL_NO_FLUSH);\n\n            if (osize != 0)\n            {\n                sys_write(z->file, z->total, buffer, (uint32_t)osize);\n                z->current->compressed += osize;\n                z->total += osize;\n            }\n            data8 += isize;\n            size -= (uint32_t)isize;\n        }\n    }\n    else\n    {\n        sys_write(z->file, z->total, data, size);\n        z->current->compressed += size;\n        z->total += size;\n    }\n}\n\nvoid zip_end_file(zip* z)\n{\n    if (z->current->compress)\n    {\n        for (;;)\n        {\n            uint8_t buffer[4096];\n\n            size_t isize = 0;\n            size_t osize = sizeof(buffer);\n            tdefl_status st = tdefl_compress(&z->tdefl, NULL, &isize, buffer, &osize, TDEFL_FINISH);\n\n            if (osize != 0)\n            {\n                sys_write(z->file, z->total, buffer, (uint32_t)osize);\n                z->current->compressed += osize;\n                z->total += osize;\n            }\n            if (st == TDEFL_STATUS_DONE)\n            {\n                break;\n            }\n        }\n    }\n\n    if (!z->crc32_set)\n    {\n        z->current->crc32 = crc32_done(&z->crc32);\n    }\n\n    if (z->current->size != 0)\n    {\n        uint8_t update[3 * sizeof(uint32_t)];\n        // crc-32\n        set32le(update + 0, z->current->crc32);\n        // compressed size\n        set32le(update + 4, (uint32_t)min64(z->current->compressed, 0xffffffff));\n        // uncompressed size\n        set32le(update + 8, (uint32_t)min64(z->current->size, 0xffffffff));\n\n        sys_write(z->file, z->current->offset + ZIP_LOCAL_HEADER_CRC32_OFFSET, update, sizeof(update));\n    }\n\n    z->current = NULL;\n}\n\nvoid zip_close(zip* z)\n{\n    uint64_t central_dir_offset = z->total;\n\n    // central directory headers\n    for (uint32_t i = 0; i < z->count; i++)\n    {\n        const zip_file* f = z->files + i;\n\n        uint8_t local[ZIP_LOCAL_HEADER_SIZE];\n        sys_read(z->file, f->offset, local, sizeof(local));\n\n        uint32_t filename_length = get16le(local + ZIP_LOCAL_HEADER_FILENAME_LENGTH_OFFSET);\n\n        uint8_t global[ZIP_GLOBAL_HEADER_SIZE + ZIP_MAX_FILENAME] = { 0x50, 0x4b, 0x01, 0x02 };\n        sys_read(z->file, f->offset + sizeof(local), global + ZIP_GLOBAL_HEADER_SIZE, filename_length);\n        int is_folder = global[ZIP_GLOBAL_HEADER_SIZE + filename_length - 1] == '/';\n\n        uint8_t extra[28];\n        uint16_t extra_size = 0;\n        uint64_t size = f->size;\n        uint64_t compressed = f->compressed;\n        uint64_t offset = f->offset;\n        uint32_t attributes = ZIP_DOS_ATTRIBUTE_ARCHIVE;\n        if (is_folder)\n        {\n            attributes |= ZIP_DOS_ATTRIBUTE_DIRECTORY;\n            if (offset > 0xffffffff)\n            {\n                extra_size += sizeof(uint64_t);\n            }\n        }\n        else\n        {\n            if (size > 0xffffffff)\n            {\n                extra_size += sizeof(uint64_t);\n            }\n            if (compressed > 0xffffffff)\n            {\n                extra_size += sizeof(uint64_t);\n            }\n            if (offset > 0xffffffff)\n            {\n                extra_size += sizeof(uint64_t);\n            }\n        }\n\n        if (extra_size)\n        {\n            extra_size += 2 * sizeof(uint16_t);\n        }\n\n        // version made by\n        set16le(global + 4, ZIP_VERSION);\n        // version needed to extract\n        set16le(global + 6, ZIP_VERSION);\n        // general purpose bit flag\n        set16le(global + 8, ZIP_UTF8_FLAG);\n        // compression method\n        set16le(global + 10, f->compress ? ZIP_METHOD_DEFLATE : ZIP_METHOD_STORE);\n        // last mod file time\n        set16le(global + 12, z->time);\n        // last mod file date\n        set16le(global + 14, z->date);\n        // crc-32\n        set32le(global + 16, f->crc32);\n        // compressed size\n        set32le(global + 20, (uint32_t)min64(compressed, 0xffffffff));\n        // uncompressed size\n        set32le(global + 24, (uint32_t)min64(size, 0xffffffff));\n        // file name length\n        set16le(global + 28, (uint16_t)filename_length);\n        // extra field length\n        set16le(global + 30, extra_size);\n        // external file attributes\n        set32le(global + 38, attributes);\n        // relative offset of local header 4 bytes\n        set32le(global + 42, (uint32_t)min64(offset, 0xffffffff));\n\n        sys_write(z->file, z->total, global, ZIP_GLOBAL_HEADER_SIZE + filename_length);\n        z->total += ZIP_GLOBAL_HEADER_SIZE + filename_length;\n\n        // zip64 Extended Information Extra Field\n        set16le(extra + 0, 1);\n        // size of this \"extra\" block\n        uint32_t extra_offset = 2 * sizeof(uint16_t);\n        set16le(extra + 2, (uint16_t)(extra_size - extra_offset));\n        if (compressed > 0xffffffff)\n        {\n            // size of compressed data\n            set64le(extra + extra_offset, compressed);\n            extra_offset += sizeof(uint64_t);\n        }\n        if (size > 0xffffffff)\n        {\n            // original uncompressed file size\n            set64le(extra + extra_offset, size);\n            extra_offset += sizeof(uint64_t);\n        }\n        if (offset > 0xffffffff)\n        {\n            // offset of local header record\n            set64le(extra + extra_offset, offset);\n            extra_offset += sizeof(uint64_t);\n        }\n\n        if (extra_size > 2 * sizeof(uint16_t))\n        {\n            sys_write(z->file, z->total, extra, extra_size);\n            z->total += extra_size;\n        }\n    }\n\n    uint64_t end_of_central_dir_offset = z->total;\n    uint64_t central_dir_size = end_of_central_dir_offset - central_dir_offset;\n\n    // zip64 end of central directory record\n    {\n        uint8_t header[ZIP64_EOC_DIR_SIZE] = { 0x50, 0x4b, 0x06, 0x06 };\n        // size of zip64 end of central directory record\n        set64le(header + 4, sizeof(header) - sizeof(uint32_t) - sizeof(uint64_t));\n        // version made by\n        set16le(header + 12, ZIP_VERSION);\n        // version needed to extract\n        set16le(header + 14, ZIP_VERSION);\n        // total number of entries in the central directory on this disk\n        set64le(header + 24, z->count);\n        // total number of entries in the central directory\n        set64le(header + 32, z->count);\n        // size of the central directory\n        set64le(header + 40, central_dir_size);\n        // offset of start of central directory with respect to the starting disk number\n        set64le(header + 48, central_dir_offset);\n\n        sys_write(z->file, z->total, header, sizeof(header));\n        z->total += sizeof(header);\n    }\n\n    // zip64 end of central directory locator\n    {\n        uint8_t header[ZIP64_EOC_DIR_LOCATOR_SIZE] = { 0x50, 0x4b, 0x06, 0x07 };\n        // relative offset of the zip64 end of central directory record 8 bytes\n        set64le(header + 8, end_of_central_dir_offset);\n        // total number of disks\n        set32le(header + 16, 1);\n\n        sys_write(z->file, z->total, header, sizeof(header));\n        z->total += sizeof(header);\n    }\n\n    // end of central directory record\n    {\n        uint8_t header[ZIP_EOC_DIR_SIZE] = { 0x50, 0x4b, 0x05, 0x06 };\n        // total number of entries in the central directory on this disk\n        set16le(header + 8, (uint16_t)min32(z->count, 0xffff));\n        // total number of entries in the central directory\n        set16le(header + 10, (uint16_t)min32(z->count, 0xffff));\n        // size of the central directory\n        set32le(header + 12, (uint32_t)min64(central_dir_size, 0xffffffff));\n        // offset of start of central directory with respect to the starting disk number\n        set32le(header + 16, (uint32_t)min64(central_dir_offset, 0xffffffff));\n\n        sys_write(z->file, z->total, header, sizeof(header));\n        z->total += sizeof(header);\n    }\n\n    sys_close(z->file);\n\n    sys_realloc(z->files, 0);\n}\n\nvoid zip_write_file_at(zip* z, uint64_t offset, const void* data, uint32_t size)\n{\n    if (z->current->compress)\n    {\n        sys_error(\"ERROR: cannot write at specific offset for compressed files\\n\");\n    }\n\n    sys_write(z->file, z->current->offset + offset, data, size);\n    z->current->size += size;\n    z->current->compressed += size;\n}\n\nvoid zip_set_offset(zip* z, uint64_t offset)\n{\n    z->total = z->current->offset + offset;\n}\n\nvoid zip_set_crc32(zip* z, uint32_t crc)\n{\n    z->current->crc32 = crc;\n    z->crc32_set = 1;\n}\n\nuint32_t zip_get_crc32(zip* z)\n{\n    return crc32_done(&z->crc32);\n}\n"
  },
  {
    "path": "pkg2zip_zip.h",
    "content": "#pragma once\n\n#include \"pkg2zip_sys.h\"\n#include \"pkg2zip_crc32.h\"\n#include \"miniz_tdef.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n\n#define ZIP_MAX_FILENAME 1024\n\ntypedef struct zip_file zip_file;\n\ntypedef struct {\n    sys_file file;\n    uint64_t total;\n    uint32_t count;\n    uint32_t max;\n    uint16_t time;\n    uint16_t date;\n    tdefl_compressor tdefl;\n    crc32_ctx crc32;\n    int crc32_set;\n    uint32_t allocated; // bytes\n    zip_file* files;\n    zip_file* current;\n} zip;\n\nvoid zip_create(zip* z, const char* name);\nvoid zip_add_folder(zip* z, const char* name);\nuint64_t zip_begin_file(zip* z, const char* name, int compress);\nvoid zip_write_file(zip* z, const void* data, uint32_t size);\nvoid zip_end_file(zip* z);\nvoid zip_close(zip* z);\n\n// hacky solution to be able to write cso header after the data is written\nvoid zip_write_file_at(zip* z, uint64_t offset, const void* data, uint32_t size);\nvoid zip_set_offset(zip* z, uint64_t offset);\nvoid zip_set_crc32(zip* z, uint32_t crc);\nuint32_t zip_get_crc32(zip* z);\n"
  },
  {
    "path": "pkg2zip_zrif.c",
    "content": "#include \"pkg2zip_zrif.h\"\n#include \"pkg2zip_utils.h\"\n#include \"pkg2zip_sys.h\"\n#include \"miniz_tdef.h\"\n#include \"puff.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#define ADLER32_MOD 65521\n\n#define ZLIB_DEFLATE_METHOD 8\n#define ZLIB_DICTIONARY_ID_ZRIF 0x627d1d5d\n\nstatic const uint8_t zrif_dict[] =\n{\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48,\n    48, 48, 54, 48, 48, 48, 48, 55, 48, 48, 48, 48, 56, 0, 48, 48, 48, 48, 51, 48, 48, 48, 48, 52, 48, 48, 48, 48,\n    53, 48, 95, 48, 48, 45, 65, 68, 68, 67, 79, 78, 84, 48, 48, 48, 48, 50, 45, 80, 67, 83, 71, 48, 48, 48, 48,\n    48, 48, 48, 48, 48, 48, 49, 45, 80, 67, 83, 69, 48, 48, 48, 45, 80, 67, 83, 70, 48, 48, 48, 45, 80, 67, 83,\n    67, 48, 48, 48, 45, 80, 67, 83, 68, 48, 48, 48, 45, 80, 67, 83, 65, 48, 48, 48, 45, 80, 67, 83, 66, 48, 48,\n    48, 0, 1, 0, 1, 0, 1, 0, 2, 239, 205, 171, 137, 103, 69, 35, 1,\n};\n\nstatic const uint8_t b64d[] =\n{\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,\n    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,\n    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,\n    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,\n    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\n    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,\n};\n\nstatic uint32_t base64_decode(const char* in, uint8_t* out)\n{\n    const uint8_t* out0 = out;\n    const uint8_t* in8 = (uint8_t*)in;\n\n    size_t len = strlen(in);\n    if (in[len - 1] == '=')\n    {\n        len--;\n    }\n    if (in[len - 1] == '=')\n    {\n        len--;\n    }\n\n    for (size_t i = 0; i < len / 4; i++)\n    {\n        *out++ = (b64d[in8[0]] << 2) + ((b64d[in8[1]] & 0x30) >> 4);\n        *out++ = (b64d[in8[1]] << 4) + (b64d[in8[2]] >> 2);\n        *out++ = (b64d[in8[2]] << 6) + b64d[in8[3]];\n        in8 += 4;\n    }\n\n    size_t left = len % 4;\n    if (left == 2)\n    {\n        *out++ = (b64d[in8[0]] << 2) + ((b64d[in8[1]] & 0x30) >> 4);\n        *out++ = (b64d[in8[1]] << 4);\n    }\n    else if (left == 3)\n    {\n        *out++ = (b64d[in8[0]] << 2) + ((b64d[in8[1]] & 0x30) >> 4);\n        *out++ = (b64d[in8[1]] << 4) + (b64d[in8[2]] >> 2);\n        *out++ = b64d[in8[2]] << 6;\n    }\n\n    return (uint32_t)(out - out0);\n}\n\n// https://www.ietf.org/rfc/rfc1950.txt\nstatic uint32_t zlib_inflate(const uint8_t* in, uint32_t inlen, uint8_t* out, uint32_t outlen)\n{\n    if (inlen < 2 + 4)\n    {\n        sys_error(\"ERROR: zRIF length too short\\n\");\n    }\n\n    if (((in[0] << 8) + in[1]) % 31 != 0)\n    {\n        sys_error(\"ERROR: zRIF header is corrupted\\n\");\n    }\n\n    if ((in[0] & 0xf) != ZLIB_DEFLATE_METHOD)\n    {\n        sys_error(\"ERROR: only deflate method supported in zRIF\\n\");\n    }\n\n    unsigned long slen = inlen - 4;\n    unsigned long dlen = outlen;\n    unsigned long dictlen = 0;\n\n    if (in[1] & (1 << 5))\n    {\n        assert(outlen > sizeof(zrif_dict));\n\n        memcpy(out, zrif_dict, sizeof(zrif_dict));\n        dictlen = sizeof(zrif_dict);\n\n        if (get32be(in + 2) != ZLIB_DICTIONARY_ID_ZRIF)\n        {\n            sys_error(\"ERROR: zRIF uses unknown dictionary\\n\");\n        }\n\n        in += 6;\n        slen -= 6;\n    }\n    else\n    {\n        in += 2;\n        slen -= 2;\n    }\n\n    if (puff(dictlen, out, &dlen, in, &slen) != 0)\n    {\n        sys_error(\"ERROR: failed to uncompress zRIF\\n\");\n    }\n    memmove(out, out + dictlen, dlen);\n\n    if (mz_adler32(MZ_ADLER32_INIT, out, dlen) != get32be(in + slen))\n    {\n        sys_error(\"ERROR: zRIF is corrupted, wrong checksum\\n\");\n    }\n\n    return dlen;\n}\n\nvoid zrif_decode(const char* str, uint8_t* rif, uint32_t rif_size)\n{\n    uint8_t raw[1024];\n    uint32_t len = base64_decode(str, raw);\n\n    uint8_t out[sizeof(zrif_dict) + 1024];\n    len = zlib_inflate(raw, len, out, sizeof(out));\n    if (len != rif_size)\n    {\n        sys_error(\"ERROR: wrong size of zRIF, is it corrupted?\\n\");\n    }\n\n    memcpy(rif, out, rif_size);\n}\n"
  },
  {
    "path": "pkg2zip_zrif.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\nvoid zrif_decode(const char* str, uint8_t* rif, uint32_t rif_size);\n"
  },
  {
    "path": "puff.c",
    "content": "/*\n * puff.c\n * Copyright (C) 2002-2013 Mark Adler\n * For conditions of distribution and use, see copyright notice in puff.h\n * version 2.3, 21 Jan 2013\n */\n\n#include <setjmp.h>             /* for setjmp(), longjmp(), and jmp_buf */\n#include \"puff.h\"               /* prototype for puff() */\n\n#define local static            /* for local function definitions */\n\n#define MAXBITS 15              /* maximum bits in a code */\n#define MAXLCODES 286           /* maximum number of literal/length codes */\n#define MAXDCODES 30            /* maximum number of distance codes */\n#define MAXCODES (MAXLCODES+MAXDCODES)  /* maximum codes lengths to read */\n#define FIXLCODES 288           /* number of fixed literal/length codes */\n\n/* input and output state */\nstruct state {\n    /* output state */\n    unsigned char *out;         /* output buffer */\n    unsigned long outlen;       /* available space at out */\n    unsigned long outcnt;       /* bytes written to out so far */\n\n    /* input state */\n    const unsigned char *in;    /* input buffer */\n    unsigned long inlen;        /* available input at in */\n    unsigned long incnt;        /* bytes read so far */\n    int bitbuf;                 /* bit buffer */\n    int bitcnt;                 /* number of bits in bit buffer */\n\n    /* input limit error return state for bits() and decode() */\n    jmp_buf env;\n};\n\nlocal int bits(struct state *s, int need)\n{\n    long val;           /* bit accumulator (can use up to 20 bits) */\n\n    /* load at least need bits into val */\n    val = s->bitbuf;\n    while (s->bitcnt < need) {\n        if (s->incnt == s->inlen)\n            longjmp(s->env, 1);         /* out of input */\n        val |= (long)(s->in[s->incnt++]) << s->bitcnt;  /* load eight bits */\n        s->bitcnt += 8;\n    }\n\n    /* drop need bits and update buffer, always zero to seven bits left */\n    s->bitbuf = (int)(val >> need);\n    s->bitcnt -= need;\n\n    /* return need bits, zeroing the bits above that */\n    return (int)(val & ((1L << need) - 1));\n}\n\nlocal int stored(struct state *s)\n{\n    unsigned len;       /* length of stored block */\n\n    /* discard leftover bits from current byte (assumes s->bitcnt < 8) */\n    s->bitbuf = 0;\n    s->bitcnt = 0;\n\n    /* get length and check against its one's complement */\n    if (s->incnt + 4 > s->inlen)\n        return 2;                               /* not enough input */\n    len = s->in[s->incnt++];\n    len |= s->in[s->incnt++] << 8;\n    if (s->in[s->incnt++] != (~len & 0xff) ||\n        s->in[s->incnt++] != ((~len >> 8) & 0xff))\n        return -2;                              /* didn't match complement! */\n\n    /* copy len bytes from in to out */\n    if (s->incnt + len > s->inlen)\n        return 2;                               /* not enough input */\n    if (s->out != NIL) {\n        if (s->outcnt + len > s->outlen)\n            return 1;                           /* not enough output space */\n        while (len--)\n            s->out[s->outcnt++] = s->in[s->incnt++];\n    }\n    else {                                      /* just scanning */\n        s->outcnt += len;\n        s->incnt += len;\n    }\n\n    /* done with a valid stored block */\n    return 0;\n}\n\nstruct huffman {\n    short *count;       /* number of symbols of each length */\n    short *symbol;      /* canonically ordered symbols */\n};\n\nlocal int decode(struct state *s, const struct huffman *h)\n{\n    int len;            /* current number of bits in code */\n    int code;           /* len bits being decoded */\n    int first;          /* first code of length len */\n    int count;          /* number of codes of length len */\n    int index;          /* index of first code of length len in symbol table */\n    int bitbuf;         /* bits from stream */\n    int left;           /* bits left in next or left to process */\n    short *next;        /* next number of codes */\n\n    bitbuf = s->bitbuf;\n    left = s->bitcnt;\n    code = first = index = 0;\n    len = 1;\n    next = h->count + 1;\n    while (1) {\n        while (left--) {\n            code |= bitbuf & 1;\n            bitbuf >>= 1;\n            count = *next++;\n            if (code - count < first) { /* if length len, return symbol */\n                s->bitbuf = bitbuf;\n                s->bitcnt = (s->bitcnt - len) & 7;\n                return h->symbol[index + (code - first)];\n            }\n            index += count;             /* else update for next length */\n            first += count;\n            first <<= 1;\n            code <<= 1;\n            len++;\n        }\n        left = (MAXBITS+1) - len;\n        if (left == 0)\n            break;\n        if (s->incnt == s->inlen)\n            longjmp(s->env, 1);         /* out of input */\n        bitbuf = s->in[s->incnt++];\n        if (left > 8)\n            left = 8;\n    }\n    return -10;                         /* ran out of codes */\n}\n\nlocal int construct(struct huffman *h, const short *length, int n)\n{\n    int symbol;         /* current symbol when stepping through length[] */\n    int len;            /* current length when stepping through h->count[] */\n    int left;           /* number of possible codes left of current length */\n    short offs[MAXBITS+1];      /* offsets in symbol table for each length */\n\n    /* count number of codes of each length */\n    for (len = 0; len <= MAXBITS; len++)\n        h->count[len] = 0;\n    for (symbol = 0; symbol < n; symbol++)\n        (h->count[length[symbol]])++;   /* assumes lengths are within bounds */\n    if (h->count[0] == n)               /* no codes! */\n        return 0;                       /* complete, but decode() will fail */\n\n    /* check for an over-subscribed or incomplete set of lengths */\n    left = 1;                           /* one possible code of zero length */\n    for (len = 1; len <= MAXBITS; len++) {\n        left <<= 1;                     /* one more bit, double codes left */\n        left -= h->count[len];          /* deduct count from possible codes */\n        if (left < 0)\n            return left;                /* over-subscribed--return negative */\n    }                                   /* left > 0 means incomplete */\n\n    /* generate offsets into symbol table for each length for sorting */\n    offs[1] = 0;\n    for (len = 1; len < MAXBITS; len++)\n        offs[len + 1] = offs[len] + h->count[len];\n\n    /*\n     * put symbols in table sorted by length, by symbol order within each\n     * length\n     */\n    for (symbol = 0; symbol < n; symbol++)\n        if (length[symbol] != 0)\n            h->symbol[offs[length[symbol]]++] = (short)symbol;\n\n    /* return zero for complete set, positive for incomplete set */\n    return left;\n}\n\nlocal int codes(struct state *s,\n                const struct huffman *lencode,\n                const struct huffman *distcode)\n{\n    int symbol;         /* decoded symbol */\n    int len;            /* length for copy */\n    unsigned dist;      /* distance for copy */\n    static const short lens[29] = { /* Size base for length codes 257..285 */\n        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,\n        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};\n    static const short lext[29] = { /* Extra bits for length codes 257..285 */\n        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,\n        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};\n    static const short dists[30] = { /* Offset base for distance codes 0..29 */\n        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,\n        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,\n        8193, 12289, 16385, 24577};\n    static const short dext[30] = { /* Extra bits for distance codes 0..29 */\n        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,\n        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,\n        12, 12, 13, 13};\n\n    /* decode literals and length/distance pairs */\n    do {\n        symbol = decode(s, lencode);\n        if (symbol < 0)\n            return symbol;              /* invalid symbol */\n        if (symbol < 256) {             /* literal: symbol is the byte */\n            /* write out the literal */\n            if (s->out != NIL) {\n                if (s->outcnt == s->outlen)\n                    return 1;\n                s->out[s->outcnt] = (unsigned char)symbol;\n            }\n            s->outcnt++;\n        }\n        else if (symbol > 256) {        /* length */\n            /* get and compute length */\n            symbol -= 257;\n            if (symbol >= 29)\n                return -10;             /* invalid fixed code */\n            len = lens[symbol] + bits(s, lext[symbol]);\n\n            /* get and check distance */\n            symbol = decode(s, distcode);\n            if (symbol < 0)\n                return symbol;          /* invalid symbol */\n            dist = dists[symbol] + bits(s, dext[symbol]);\n#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n            if (dist > s->outcnt)\n                return -11;     /* distance too far back */\n#endif\n\n            /* copy length bytes from distance bytes back */\n            if (s->out != NIL) {\n                if (s->outcnt + len > s->outlen)\n                    return 1;\n                while (len--) {\n                    s->out[s->outcnt] =\n#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n                        dist > s->outcnt ?\n                            0 :\n#endif\n                            s->out[s->outcnt - dist];\n                    s->outcnt++;\n                }\n            }\n            else\n                s->outcnt += len;\n        }\n    } while (symbol != 256);            /* end of block symbol */\n\n    /* done with a valid fixed or dynamic block */\n    return 0;\n}\n\nlocal int fixed(struct state *s)\n{\n    static int virgin = 1;\n    static short lencnt[MAXBITS+1], lensym[FIXLCODES];\n    static short distcnt[MAXBITS+1], distsym[MAXDCODES];\n    static struct huffman lencode, distcode;\n\n    /* build fixed huffman tables if first call (may not be thread safe) */\n    if (virgin) {\n        int symbol;\n        short lengths[FIXLCODES];\n\n        /* construct lencode and distcode */\n        lencode.count = lencnt;\n        lencode.symbol = lensym;\n        distcode.count = distcnt;\n        distcode.symbol = distsym;\n\n        /* literal/length table */\n        for (symbol = 0; symbol < 144; symbol++)\n            lengths[symbol] = 8;\n        for (; symbol < 256; symbol++)\n            lengths[symbol] = 9;\n        for (; symbol < 280; symbol++)\n            lengths[symbol] = 7;\n        for (; symbol < FIXLCODES; symbol++)\n            lengths[symbol] = 8;\n        construct(&lencode, lengths, FIXLCODES);\n\n        /* distance table */\n        for (symbol = 0; symbol < MAXDCODES; symbol++)\n            lengths[symbol] = 5;\n        construct(&distcode, lengths, MAXDCODES);\n\n        /* do this just once */\n        virgin = 0;\n    }\n\n    /* decode data until end-of-block code */\n    return codes(s, &lencode, &distcode);\n}\n\nlocal int dynamic(struct state *s)\n{\n    int nlen, ndist, ncode;             /* number of lengths in descriptor */\n    int index;                          /* index of lengths[] */\n    int err;                            /* construct() return value */\n    short lengths[MAXCODES];            /* descriptor code lengths */\n    short lencnt[MAXBITS+1], lensym[MAXLCODES];         /* lencode memory */\n    short distcnt[MAXBITS+1], distsym[MAXDCODES];       /* distcode memory */\n    struct huffman lencode, distcode;   /* length and distance codes */\n    static const short order[19] =      /* permutation of code length codes */\n        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};\n\n    /* construct lencode and distcode */\n    lencode.count = lencnt;\n    lencode.symbol = lensym;\n    distcode.count = distcnt;\n    distcode.symbol = distsym;\n\n    /* get number of lengths in each table, check lengths */\n    nlen = bits(s, 5) + 257;\n    ndist = bits(s, 5) + 1;\n    ncode = bits(s, 4) + 4;\n    if (nlen > MAXLCODES || ndist > MAXDCODES)\n        return -3;                      /* bad counts */\n\n    /* read code length code lengths (really), missing lengths are zero */\n    for (index = 0; index < ncode; index++)\n        lengths[order[index]] = (short)bits(s, 3);\n    for (; index < 19; index++)\n        lengths[order[index]] = 0;\n\n    /* build huffman table for code lengths codes (use lencode temporarily) */\n    err = construct(&lencode, lengths, 19);\n    if (err != 0)               /* require complete code set here */\n        return -4;\n\n    /* read length/literal and distance code length tables */\n    index = 0;\n    while (index < nlen + ndist) {\n        int symbol;             /* decoded value */\n        int len;                /* last length to repeat */\n\n        symbol = decode(s, &lencode);\n        if (symbol < 0)\n            return symbol;          /* invalid symbol */\n        if (symbol < 16)                /* length in 0..15 */\n            lengths[index++] = (short)symbol;\n        else {                          /* repeat instruction */\n            len = 0;                    /* assume repeating zeros */\n            if (symbol == 16) {         /* repeat last length 3..6 times */\n                if (index == 0)\n                    return -5;          /* no last length! */\n                len = lengths[index - 1];       /* last length */\n                symbol = 3 + bits(s, 2);\n            }\n            else if (symbol == 17)      /* repeat zero 3..10 times */\n                symbol = 3 + bits(s, 3);\n            else                        /* == 18, repeat zero 11..138 times */\n                symbol = 11 + bits(s, 7);\n            if (index + symbol > nlen + ndist)\n                return -6;              /* too many lengths! */\n            while (symbol--)            /* repeat last or zero symbol times */\n                lengths[index++] = (short)len;\n        }\n    }\n\n    /* check for end-of-block code -- there better be one! */\n    if (lengths[256] == 0)\n        return -9;\n\n    /* build huffman table for literal/length codes */\n    err = construct(&lencode, lengths, nlen);\n    if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1]))\n        return -7;      /* incomplete code ok only for single length 1 code */\n\n    /* build huffman table for distance codes */\n    err = construct(&distcode, lengths + nlen, ndist);\n    if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1]))\n        return -8;      /* incomplete code ok only for single length 1 code */\n\n    /* decode data until end-of-block code */\n    return codes(s, &lencode, &distcode);\n}\n\nint puff(unsigned long dictlen,         // length of custom dictionary\n         unsigned char *dest,           /* pointer to destination pointer */\n         unsigned long *destlen,        /* amount of output space */\n         const unsigned char *source,   /* pointer to source data pointer */\n         unsigned long *sourcelen)      /* amount of input available */\n{\n    struct state s;             /* input/output state */\n    int last, type;             /* block information */\n    int err;                    /* return value */\n\n    /* initialize output state */\n    s.out = dest;\n    s.outlen = *destlen;                /* ignored if dest is NIL */\n    s.outcnt = dictlen;\n\n    /* initialize input state */\n    s.in = source;\n    s.inlen = *sourcelen;\n    s.incnt = 0;\n    s.bitbuf = 0;\n    s.bitcnt = 0;\n\n    /* return if bits() or decode() tries to read past available input */\n    if (setjmp(s.env) != 0)             /* if came back here via longjmp() */\n        err = 2;                        /* then skip do-loop, return error */\n    else {\n        /* process blocks until last block or error */\n        do {\n            last = bits(&s, 1);         /* one if last block */\n            type = bits(&s, 2);         /* block type 0..3 */\n            err = type == 0 ?\n                    stored(&s) :\n                    (type == 1 ?\n                        fixed(&s) :\n                        (type == 2 ?\n                            dynamic(&s) :\n                            -1));       /* type == 3, invalid */\n            if (err != 0)\n                break;                  /* return with error */\n        } while (!last);\n    }\n\n    /* update the lengths and return */\n    if (err <= 0) {\n        *destlen = s.outcnt - dictlen;\n        *sourcelen = s.incnt;\n    }\n    return err;\n}\n"
  },
  {
    "path": "puff.h",
    "content": "/* puff.h\n  Copyright (C) 2002-2013 Mark Adler, all rights reserved\n  version 2.3, 21 Jan 2013\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the author be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n\n  Mark Adler    madler@alumni.caltech.edu\n */\n\n// Extra modifications to support custom dictionary for pkg2zip\n\n/*\n * See puff.c for purpose and usage.\n */\n#ifndef NIL\n#  define NIL ((unsigned char *)0)      /* for no output option */\n#endif\n\nint puff(unsigned long dictlen,         // length of custom dictionary (must be placed in beginning of dest)\n         unsigned char *dest,           /* pointer to destination pointer */\n         unsigned long *destlen,        /* amount of output space */\n         const unsigned char *source,   /* pointer to source data pointer */\n         unsigned long *sourcelen);     /* amount of input available */\n"
  },
  {
    "path": "rif2zrif.py",
    "content": "#!/usr/bin/env python3\n\nimport sys\nimport zlib\nimport base64\n\nzrif_dict = list(zlib.decompress(base64.b64decode(\n  b\"eNpjYBgFo2AU0AsYAIElGt8MRJiDCAsw3xhEmIAIU4N4AwNdRxcXZ3+/EJCAkW6Ac7C7ARwYgviuQAaIdoPSzlDaBUo7QmknIM3ACIZM78+u7kx3VWYEAGJ9HV0=\")))\n\nif len(sys.argv) != 2:\n  exit(\"Usage: %s path/to/file.rif\" % sys.argv[0])\n\nrif = open(sys.argv[1], \"rb\").read()\n\nc = zlib.compressobj(level=9, wbits=10, memLevel=8, zdict=bytes(zrif_dict))\nbin = c.compress(rif)\nbin += c.flush()\n\nif len(bin) % 3 != 0:\n  bin += b\"\\0\" * (3 - len(bin) % 3)\n\ncontent = rif[0x10:0x40].rstrip(b\"\\0\").decode(\"ascii\")\n\nprint(content, base64.b64encode(bin).decode(\"ascii\"))\n"
  },
  {
    "path": "zrif2rif.py",
    "content": "#!/usr/bin/env python3\n\nimport sys\nimport zlib\nimport base64\n\nzrif_dict = list(zlib.decompress(base64.b64decode(\n  b\"eNpjYBgFo2AU0AsYAIElGt8MRJiDCAsw3xhEmIAIU4N4AwNdRxcXZ3+/EJCAkW6Ac7C7ARwYgviuQAaIdoPSzlDaBUo7QmknIM3ACIZM78+u7kx3VWYEAGJ9HV0=\")))\n\nif len(sys.argv) != 2 and len(sys.argv) != 3:\n  exit(\"Usage: %s zRIF [path/to/work.bin]\" % sys.argv[0])\n\nbin = base64.b64decode(sys.argv[1].encode(\"ascii\"))\n\nd = zlib.decompressobj(wbits=10, zdict=bytes(zrif_dict))\nrif = d.decompress(bin)\nrif += d.flush()\n\noutput = sys.argv[2] if len(sys.argv) == 3 else \"work.bin\"\nopen(output, \"wb\").write(rif)\n"
  }
]