diff --git a/.gitignore b/.gitignore index a6e1ecc1..26fa2ad5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ zig-out/ zig-cache/ +.build_config/ .zigmod/ kcov-output/ macos-sdk/ diff --git a/android/.gitattributes b/android/.gitattributes new file mode 100644 index 00000000..49e81254 --- /dev/null +++ b/android/.gitattributes @@ -0,0 +1 @@ +*.zig text=auto eol=lf \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 00000000..c6f6a0ab --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,6 @@ +zig-cache +*.apk +*.keystore +.build_config +zig-out +*.apk.idsig diff --git a/android/LICENSE b/android/LICENSE new file mode 100644 index 00000000..4c35f4e4 --- /dev/null +++ b/android/LICENSE @@ -0,0 +1,8 @@ +Copyright (c) 2020 Felix "xq" Queißner +https://github.com/MasterQ32/ZigAndroidTemplate + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/android/README.md b/android/README.md new file mode 100644 index 00000000..7873863b --- /dev/null +++ b/android/README.md @@ -0,0 +1,64 @@ +# Android Apps in Zig + +![Project banner](design/logo.png) + +This repository contains a example on how to create a minimal Android app in Zig. + +## State of the project + +This project contains a really small app skeleton in `example/main.zig` which initializes OpenGL and renders a color cycle. Touchscreen events will be displayed as small circles beneath the fingers that will fade as soon as no event for the same finger will happen again. + +The code contains some commented examples on how to interface with the JNI to use advanced features of the `ANativeActivity`. + +It has no dependencies to C code except for the android libraries, so it can be considered a pure Zig app. + +We're running a CI that will verify the build for Windows, macOS and Linux: + +[![CI](https://github.com/MasterQ32/ZigAndroidTemplate/actions/workflows/main-ci.yml/badge.svg)](https://github.com/MasterQ32/ZigAndroidTemplate/actions/workflows/main-ci.yml) + +## Presentation + +There is a [FOSDEM Talk](https://fosdem.org/2021/schedule/event/zig_android/) you can watch here: + +- [MP4 Video](https://video.fosdem.org/2021/D.zig/zig_android.mp4) +- [WebM Video](https://video.fosdem.org/2021/D.zig/zig_android.webm) + +## What's missing + +- Configuration management example +- Save/load app state example + +## Requirements & Build + +You need the [Android SDK](https://developer.android.com/studio#command-tools) installed together with the [Android NDK](https://developer.android.com/ndk). + +You also need [adb](https://developer.android.com/studio/command-line/adb) and a Java SDK installed (required for `jarsigner`). + +Now you need to generate yourself a keystore to sign your apps. For debugging purposes, the build script contains a helper. Just invoke `zig build keystore` to generate yourself a debug keystore that can be used with later build invocations. + +**Note** that the build file might ask you to configure some paths. Do as requested and just run the build again, it should work then. + +If all of the above is done, you should be able to build the app by running `zig build`. + +There are convenience options with `zig build push` (installs the app on a connected phone) and `zig build run` (which installs, then runs the app). + +### Quick Start + +Install the [`sdkmanager`](https://developer.android.com/studio/command-line/sdkmanager) and invoke the following command line: + +``` +sdkmanager --install "platforms;android-21" # Min version: Android 5 +sdkmanager --install "build-tools;33.0.0" +sdkmanager --install "ndk;25.1.8937393" +zig build keystore install run +``` + +This should build an APK and install it on your connected phone if possible. + +## Getting started + +Check out the [`build.zig`](build.zig) to see how to build a new android app, and [`example/main.zig`](example/main.zig) to see how to create a basic application. All of this is still very rough, though. + +## Credits + +Huge thanks to [@cnlohr](https://github.com/cnlohr) to create [rawdrawandroid](https://github.com/cnlohr/rawdrawandroid) and making this project possible! diff --git a/android/Sdk.zig b/android/Sdk.zig new file mode 100644 index 00000000..3a6428d6 --- /dev/null +++ b/android/Sdk.zig @@ -0,0 +1,1427 @@ +//! External dependencies: +//! - `keytool` from OpenJDK +//! - `apksigner`, `aapt`, `zipalign`, and `adb` from the Android tools package + +const std = @import("std"); +const builtin = @import("builtin"); + +const auto_detect = @import("build/auto-detect.zig"); + +fn sdkRootIntern() []const u8 { + return std.fs.path.dirname(@src().file) orelse "."; +} + +fn sdkRoot() *const [sdkRootIntern().len]u8 { + comptime var buffer = sdkRootIntern(); + return buffer[0..buffer.len]; +} + +// linux-x86_64 +pub fn toolchainHostTag() []const u8 { + comptime { + const os = builtin.os.tag; + const arch = builtin.cpu.arch; + return @tagName(os) ++ "-" ++ @tagName(arch); + } +} + +/// This file encodes a instance of an Android SDK interface. +const Sdk = @This(); + +/// The builder instance associated with this object. +b: *Builder, + +/// A set of tools that run on the build host that are required to complete the +/// project build. Must be created with the `hostTools()` function that passes in +/// the correct relpath to the package. +host_tools: HostTools, + +/// The configuration for all non-shipped system tools. +/// Contains the normal default config for each tool. +system_tools: SystemTools = .{}, + +/// Contains paths to each required input folder. +folders: UserConfig, + +versions: ToolchainVersions, + +/// Initializes the android SDK. +/// It requires some input on which versions of the tool chains should be used +pub fn init(b: *Builder, user_config: ?UserConfig, toolchains: ToolchainVersions) *Sdk { + const actual_user_config = user_config orelse auto_detect.findUserConfig(b, toolchains) catch |err| @panic(@errorName(err)); + + const system_tools = blk: { + const exe = if (builtin.os.tag == .windows) ".exe" else ""; + + const zipalign = std.fs.path.join(b.allocator, &[_][]const u8{ actual_user_config.android_sdk_root, "build-tools", toolchains.build_tools_version, "zipalign" ++ exe }) catch unreachable; + const aapt = std.fs.path.join(b.allocator, &[_][]const u8{ actual_user_config.android_sdk_root, "build-tools", toolchains.build_tools_version, "aapt" ++ exe }) catch unreachable; + const adb = std.fs.path.join(b.allocator, &[_][]const u8{ actual_user_config.android_sdk_root, "platform-tools", "adb" ++ exe }) catch unreachable; + const apksigner = std.fs.path.join(b.allocator, &[_][]const u8{ actual_user_config.android_sdk_root, "build-tools", toolchains.build_tools_version, "apksigner" ++ exe }) catch unreachable; + const keytool = std.fs.path.join(b.allocator, &[_][]const u8{ actual_user_config.java_home, "bin", "keytool" ++ exe }) catch unreachable; + + break :blk SystemTools{ + .zipalign = zipalign, + .aapt = aapt, + .adb = adb, + .apksigner = apksigner, + .keytool = keytool, + }; + }; + + // Compiles all required additional tools for toolchain. + const host_tools = blk: { + const zip_add = b.addExecutable("zip_add", sdkRoot() ++ "/tools/zip_add.zig"); + zip_add.addCSourceFile(sdkRoot() ++ "/vendor/kuba-zip/zip.c", &[_][]const u8{ + "-std=c99", + "-fno-sanitize=undefined", + "-D_POSIX_C_SOURCE=200112L", + }); + zip_add.addIncludePath(sdkRoot() ++ "/vendor/kuba-zip"); + zip_add.linkLibC(); + + break :blk HostTools{ + .zip_add = zip_add, + }; + }; + + const sdk = b.allocator.create(Sdk) catch @panic("out of memory"); + sdk.* = Sdk{ + .b = b, + .host_tools = host_tools, + .system_tools = system_tools, + .folders = actual_user_config, + .versions = toolchains, + }; + return sdk; +} + +pub const ToolchainVersions = struct { + build_tools_version: []const u8 = "33.0.0", + ndk_version: []const u8 = "25.1.8937393", +}; + +pub const AndroidVersion = enum(u16) { + android4 = 19, // KitKat + android5 = 21, // Lollipop + android6 = 23, // Marshmallow + android7 = 24, // Nougat + android8 = 26, // Oreo + android9 = 28, // Pie + android10 = 29, // Quince Tart + android11 = 30, // Red Velvet Cake + android12 = 31, // Snow Cone + android13 = 33, // Tiramisu + + _, // we allow to overwrite the defaults +}; + +pub const UserConfig = struct { + android_sdk_root: []const u8 = "", + android_ndk_root: []const u8 = "", + java_home: []const u8 = "", +}; + +/// Configuration of the Android toolchain. +pub const Config = struct { + /// Path to the SDK root folder. + /// Example: `/home/ziggy/android-sdk`. + sdk_root: []const u8, + + /// Path to the NDK root folder. + /// Example: `/home/ziggy/android-sdk/ndk/21.1.6352462`. + ndk_root: []const u8, + + /// Path to the build tools folder. + /// Example: `/home/ziggy/android-sdk/build-tools/28.0.3`. + build_tools: []const u8, + + /// A key store. This is required when an APK is created and signed. + /// If you don't care for production code, just use the default here + /// and it will work. This needs to be changed to a *proper* key store + /// when you want to publish the app. + key_store: KeyStore = KeyStore{ + .file = "zig-cache/", + .alias = "default", + .password = "ziguana", + }, +}; + +/// A resource that will be packed into the appliation. +pub const Resource = struct { + /// This is the relative path to the resource root + path: []const u8, + /// This is the content of the file. + content: std.build.FileSource, +}; + +/// Configuration of an application. +pub const AppConfig = struct { + /// The display name of the application. This is shown to the users. + display_name: []const u8, + + /// Application name, only lower case letters and underscores are allowed. + app_name: []const u8, + + /// Java package name, usually the reverse top level domain + app name. + /// Only lower case letters, dots and underscores are allowed. + package_name: []const u8, + + /// The android version which is embedded in the manifset. + /// The default is Android 9, it's more than 4 years old by now and should be widespread enough to be a reasonable default. + target_version: AndroidVersion = .android9, + + /// The resource directory that will contain the manifest and other app resources. + /// This should be a distinct directory per app. + resources: []const Resource = &[_]Resource{}, + + /// If true, the app will be started in "fullscreen" mode, this means that + /// navigation buttons as well as the top bar are not shown. + /// This is usually relevant for games. + fullscreen: bool = false, + + /// If true, the app will be compiled with the AAudio library. + aaudio: bool = false, + + /// If true, the app will be compiled with the OpenSL library + opensl: bool = true, + + /// One or more asset directories. Each directory will be added into the app assets. + asset_directories: []const []const u8 = &[_][]const u8{}, + + permissions: []const []const u8 = &[_][]const u8{ + //"android.permission.SET_RELEASE_APP", + //"android.permission.RECORD_AUDIO", + }, + + libraries: []const []const u8 = &app_libs, + + packages: []const std.build.Pkg = &.{}, +}; + +/// One of the legal targets android can be built for. +pub const Target = enum { + aarch64, + arm, + x86, + x86_64, +}; + +pub const KeyStore = struct { + file: []const u8, + alias: []const u8, + password: []const u8, +}; + +pub const HostTools = struct { + zip_add: *std.build.LibExeObjStep, +}; + +/// Configuration of the binary paths to all tools that are not included in the android SDK. +pub const SystemTools = struct { + mkdir: []const u8 = "mkdir", + rm: []const u8 = "rm", + + zipalign: []const u8 = "zipalign", + aapt: []const u8 = "aapt", + adb: []const u8 = "adb", + apksigner: []const u8 = "apksigner", + keytool: []const u8 = "keytool", +}; + +/// The configuration which targets a app should be built for. +pub const AppTargetConfig = struct { + aarch64: ?bool = null, + arm: ?bool = null, + x86_64: ?bool = null, + x86: ?bool = null, +}; + +pub const CreateAppStep = struct { + sdk: *Sdk, + first_step: *std.build.Step, + final_step: *std.build.Step, + + libraries: []const *std.build.LibExeObjStep, + build_options: *BuildOptionStep, + + apk_file: std.build.FileSource, + + package_name: []const u8, + + pub fn getAndroidPackage(self: @This(), name: []const u8) std.build.Pkg { + return self.sdk.b.dupePkg(std.build.Pkg{ + .name = name, + .source = .{ .path = sdkRoot() ++ "/src/android-support.zig" }, + .dependencies = &[_]std.build.Pkg{ + self.build_options.getPackage("build_options"), + }, + }); + } + + pub fn install(self: @This()) *Step { + return self.sdk.installApp(self.apk_file); + } + + pub fn run(self: @This()) *Step { + return self.sdk.startApp(self.package_name); + } +}; + +const NdkVersionRange = struct { + ndk: []const u8, + min: u16, + max: u16, + + pub fn validate(range: []const NdkVersionRange, ndk: []const u8, api: u16) void { + const ndk_version = std.SemanticVersion.parse(ndk) catch { + std.debug.print("Could not parse NDK version {s} as semantic version. Could not perform NDK validation!\n", .{ndk}); + return; + }; + std.debug.assert(range.len > 0); + + for (range) |vers| { + const r_version = std.SemanticVersion.parse(vers.ndk) catch unreachable; + if (ndk_version.order(r_version) == .eq) { + // Perfect version match + if (api < vers.min) { + std.debug.print("WARNING: Selected NDK {s} does not support api level {d}. Minimum supported version is {d}!\n", .{ + ndk, + api, + vers.min, + }); + } + if (api > vers.max) { + std.debug.print("WARNING: Selected NDK {s} does not support api level {d}. Maximum supported version is {d}!\n", .{ + ndk, + api, + vers.max, + }); + } + } + return; + } + + // NDK old X => min=5, max=8 + // NDK now Y => api=7 + // NDK future Z => min=6, max=13 + + var older_version: NdkVersionRange = range[0]; // biggest Y <= X + for (range[1..]) |vers| { + const r_version = std.SemanticVersion.parse(vers.ndk) catch unreachable; + if (r_version.order(ndk_version) != .gt) { // r_version <= ndk_version + older_version = vers; + } else { + // range is ordered, so we know that we can't find anything smaller now anyways + break; + } + } + var newer_version: NdkVersionRange = range[range.len - 1]; // smallest Z >= X + for (range[1..]) |vers| { + const r_version = std.SemanticVersion.parse(vers.ndk) catch unreachable; + if (r_version.order(ndk_version) != .lt) { + newer_version = vers; + break; + } + } + + // take for max api, as we assume that an older NDK than Z might not support Z.max yet + if (api < newer_version.min) { + std.debug.print("WARNING: Selected NDK {s} might not support api level {d}. Minimum supported version is guessed as {d}, as NDK {s} only supports that!\n", .{ + ndk, + api, + newer_version.min, + newer_version.ndk, + }); + } + // take for min api, as we assume that a newer NDK than X might not support X.min anymore + if (api > older_version.max) { + std.debug.print("WARNING: Selected NDK {s} might not support api level {d}. Maximum supported version is guessed as {d}, as NDK {s} only supports that!\n", .{ + ndk, + api, + older_version.max, + older_version.ndk, + }); + } + } +}; + +// ls ~/software/android-sdk/ndk/*/toolchains/llvm/prebuilt/${hosttag}/sysroot/usr/lib/arm-linux-androideabi | code +const arm_ndk_ranges = [_]NdkVersionRange{ + NdkVersionRange{ .ndk = "19.2.5345600", .min = 16, .max = 28 }, + NdkVersionRange{ .ndk = "20.1.5948944", .min = 16, .max = 29 }, + NdkVersionRange{ .ndk = "21.4.7075529", .min = 16, .max = 30 }, + NdkVersionRange{ .ndk = "22.1.7171670", .min = 16, .max = 30 }, + NdkVersionRange{ .ndk = "23.2.8568313", .min = 16, .max = 31 }, + NdkVersionRange{ .ndk = "24.0.8215888", .min = 19, .max = 32 }, + NdkVersionRange{ .ndk = "25.1.8937393", .min = 19, .max = 33 }, +}; + +// ls ~/software/android-sdk/ndk/*/toolchains/llvm/prebuilt/${hosttag}/sysroot/usr/lib/i686* | code +const i686_ndk_ranges = [_]NdkVersionRange{ + NdkVersionRange{ .ndk = "19.2.5345600", .min = 16, .max = 28 }, + NdkVersionRange{ .ndk = "20.1.5948944", .min = 16, .max = 29 }, + NdkVersionRange{ .ndk = "21.4.7075529", .min = 16, .max = 30 }, + NdkVersionRange{ .ndk = "22.1.7171670", .min = 16, .max = 30 }, + NdkVersionRange{ .ndk = "23.2.8568313", .min = 16, .max = 31 }, + NdkVersionRange{ .ndk = "24.0.8215888", .min = 19, .max = 32 }, + NdkVersionRange{ .ndk = "25.1.8937393", .min = 19, .max = 33 }, +}; + +// ls ~/software/android-sdk/ndk/*/toolchains/llvm/prebuilt/${hosttag}/sysroot/usr/lib/x86_64-linux-android | code +const x86_64_ndk_ranges = [_]NdkVersionRange{ + NdkVersionRange{ .ndk = "19.2.5345600", .min = 21, .max = 28 }, + NdkVersionRange{ .ndk = "20.1.5948944", .min = 21, .max = 29 }, + NdkVersionRange{ .ndk = "21.4.7075529", .min = 21, .max = 30 }, + NdkVersionRange{ .ndk = "22.1.7171670", .min = 21, .max = 30 }, + NdkVersionRange{ .ndk = "23.2.8568313", .min = 21, .max = 31 }, + NdkVersionRange{ .ndk = "24.0.8215888", .min = 21, .max = 32 }, + NdkVersionRange{ .ndk = "25.1.8937393", .min = 21, .max = 33 }, +}; + +// ls ~/software/android-sdk/ndk/*/toolchains/llvm/prebuilt/${hosttag}/sysroot/usr/lib/aarch64-linux-android | code +const aarch64_ndk_ranges = [_]NdkVersionRange{ + NdkVersionRange{ .ndk = "19.2.5345600", .min = 21, .max = 28 }, + NdkVersionRange{ .ndk = "20.1.5948944", .min = 21, .max = 29 }, + NdkVersionRange{ .ndk = "21.4.7075529", .min = 21, .max = 30 }, + NdkVersionRange{ .ndk = "22.1.7171670", .min = 21, .max = 30 }, + NdkVersionRange{ .ndk = "23.2.8568313", .min = 21, .max = 31 }, + NdkVersionRange{ .ndk = "24.0.8215888", .min = 21, .max = 32 }, + NdkVersionRange{ .ndk = "25.1.8937393", .min = 21, .max = 33 }, +}; + +/// Instantiates the full build pipeline to create an APK file. +/// +pub fn createApp( + sdk: *Sdk, + apk_file: []const u8, + src_file: []const u8, + app_config: AppConfig, + mode: std.builtin.Mode, + wanted_targets: AppTargetConfig, + key_store: KeyStore, +) CreateAppStep { + const write_xml_step = sdk.b.addWriteFile("strings.xml", blk: { + var buf = std.ArrayList(u8).init(sdk.b.allocator); + errdefer buf.deinit(); + + var writer = buf.writer(); + + writer.writeAll( + \\ + \\ + \\ + ) catch unreachable; + + writer.print( + \\ {s} + \\ {s} + \\ {s} + \\ + , .{ + app_config.display_name, + app_config.app_name, + app_config.package_name, + }) catch unreachable; + + writer.writeAll( + \\ + \\ + ) catch unreachable; + + break :blk buf.toOwnedSlice(); + }); + + const manifest_step = sdk.b.addWriteFile("AndroidManifest.xml", blk: { + var buf = std.ArrayList(u8).init(sdk.b.allocator); + errdefer buf.deinit(); + + var writer = buf.writer(); + + @setEvalBranchQuota(1_000_000); + writer.print( + \\ + \\ + , .{app_config.package_name}) catch unreachable; + for (app_config.permissions) |perm| { + writer.print( + \\ + \\ + , .{perm}) catch unreachable; + } + + if (app_config.fullscreen) { + writer.writeAll( + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ) catch unreachable; + } else { + writer.writeAll( + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ) catch unreachable; + } + + break :blk buf.toOwnedSlice(); + }); + + const resource_dir_step = CreateResourceDirectory.create(sdk.b); + for (app_config.resources) |res| { + resource_dir_step.add(res); + } + resource_dir_step.add(Resource{ + .path = "values/strings.xml", + .content = write_xml_step.getFileSource("strings.xml").?, + }); + + const sdk_version_int = @enumToInt(app_config.target_version); + + if (sdk_version_int < 16) @panic("Minimum supported sdk version is 16."); + + const targets = AppTargetConfig{ + .aarch64 = wanted_targets.aarch64 orelse (sdk_version_int >= 21), + .x86_64 = wanted_targets.x86_64 orelse (sdk_version_int >= 21), + .x86 = wanted_targets.x86 orelse (sdk_version_int >= 16), + .arm = wanted_targets.arm orelse (sdk_version_int >= 16), + }; + + // These are hard assumptions + if (targets.aarch64.? and sdk_version_int < 21) @panic("Aarch64 android is only available since sdk version 21."); + if (targets.x86_64.? and sdk_version_int < 21) @panic("x86_64 android is only available since sdk version 21."); + if (targets.x86.? and sdk_version_int < 16) @panic("x86 android is only available since sdk version 16."); + if (targets.arm.? and sdk_version_int < 16) @panic("arm android is only available since sdk version 16."); + + // Also perform a soft check for known NDK versions + if (targets.aarch64.?) NdkVersionRange.validate(&aarch64_ndk_ranges, sdk.versions.ndk_version, sdk_version_int); + if (targets.x86_64.?) NdkVersionRange.validate(&x86_64_ndk_ranges, sdk.versions.ndk_version, sdk_version_int); + if (targets.x86.?) NdkVersionRange.validate(&x86_64_ndk_ranges, sdk.versions.ndk_version, sdk_version_int); + if (targets.arm.?) NdkVersionRange.validate(&arm_ndk_ranges, sdk.versions.ndk_version, sdk_version_int); + + const root_jar = std.fs.path.resolve(sdk.b.allocator, &[_][]const u8{ + sdk.folders.android_sdk_root, + "platforms", + sdk.b.fmt("android-{d}", .{sdk_version_int}), + "android.jar", + }) catch unreachable; + + const unaligned_apk_file = sdk.b.pathJoin(&.{ + sdk.b.build_root, + sdk.b.cache_root, + sdk.b.fmt("unaligned-{s}", .{std.fs.path.basename(apk_file)}), + }); + + const make_unsigned_apk = sdk.b.addSystemCommand(&[_][]const u8{ + sdk.system_tools.aapt, + "package", + "-f", // force overwrite of existing files + "-F", // specify the apk file to output + sdk.b.pathFromRoot(unaligned_apk_file), + "-I", // add an existing package to base include set + root_jar, + }); + + make_unsigned_apk.addArg("-M"); // specify full path to AndroidManifest.xml to include in zip + make_unsigned_apk.addFileSourceArg(manifest_step.getFileSource("AndroidManifest.xml").?); + + make_unsigned_apk.addArg("-S"); // directory in which to find resources. Multiple directories will be scanned and the first match found (left to right) will take precedence + make_unsigned_apk.addFileSourceArg(resource_dir_step.getOutputDirectory()); + + make_unsigned_apk.addArgs(&[_][]const u8{ + "-v", + "--target-sdk-version", + sdk.b.fmt("{d}", .{sdk_version_int}), + }); + for (app_config.asset_directories) |dir| { + make_unsigned_apk.addArg("-A"); // additional directory in which to find raw asset files + make_unsigned_apk.addArg(sdk.b.pathFromRoot(dir)); + } + + var libs = std.ArrayList(*std.build.LibExeObjStep).init(sdk.b.allocator); + defer libs.deinit(); + + const build_options = BuildOptionStep.create(sdk.b); + build_options.add([]const u8, "app_name", app_config.app_name); + build_options.add(u16, "android_sdk_version", sdk_version_int); + build_options.add(bool, "fullscreen", app_config.fullscreen); + build_options.add(bool, "enable_aaudio", app_config.aaudio); + build_options.add(bool, "enable_opensl", app_config.opensl); + + const align_step = sdk.alignApk(unaligned_apk_file, apk_file); + + const sign_step = sdk.signApk(apk_file, key_store); + sign_step.dependOn(align_step); + + inline for (std.meta.fields(AppTargetConfig)) |fld| { + const target_name = @field(Target, fld.name); + if (@field(targets, fld.name).?) { + const step = sdk.compileAppLibrary( + src_file, + app_config, + mode, + target_name, + // build_options.getPackage("build_options"), + ); + libs.append(step) catch unreachable; + + // https://developer.android.com/ndk/guides/abis#native-code-in-app-packages + const so_dir = switch (target_name) { + .aarch64 => "lib/arm64-v8a/", + .arm => "lib/armeabi-v7a/", + .x86_64 => "lib/x86_64/", + .x86 => "lib/x86/", + }; + + const copy_to_zip = CopyToZipStep.create(sdk, unaligned_apk_file, so_dir, step.getOutputSource()); + copy_to_zip.step.dependOn(&make_unsigned_apk.step); // enforces creation of APK before the execution + align_step.dependOn(©_to_zip.step); + } + } + + // const compress_step = compressApk(b, android_config, apk_file, "zig-out/demo.packed.apk"); + // compress_step.dependOn(sign_step); + + return CreateAppStep{ + .sdk = sdk, + .first_step = &make_unsigned_apk.step, + .final_step = sign_step, + .libraries = libs.toOwnedSlice(), + .build_options = build_options, + .package_name = sdk.b.dupe(app_config.package_name), + .apk_file = (std.build.FileSource{ .path = apk_file }).dupe(sdk.b), + }; +} + +const CreateResourceDirectory = struct { + const Self = @This(); + builder: *std.build.Builder, + step: std.build.Step, + + resources: std.ArrayList(Resource), + directory: std.build.GeneratedFile, + + pub fn create(b: *std.build.Builder) *Self { + const self = b.allocator.create(Self) catch @panic("out of memory"); + self.* = Self{ + .builder = b, + .step = Step.init(.custom, "populate resource directory", b.allocator, CreateResourceDirectory.make), + .directory = .{ .step = &self.step }, + .resources = std.ArrayList(Resource).init(b.allocator), + }; + return self; + } + + pub fn add(self: *Self, resource: Resource) void { + self.resources.append(Resource{ + .path = self.builder.dupe(resource.path), + .content = resource.content.dupe(self.builder), + }) catch @panic("out of memory"); + resource.content.addStepDependencies(&self.step); + } + + pub fn getOutputDirectory(self: *Self) std.build.FileSource { + return .{ .generated = &self.directory }; + } + + fn make(step: *Step) !void { + const self = @fieldParentPtr(Self, "step", step); + + // if (std.fs.path.dirname(strings_xml)) |dir| { + // std.fs.cwd().makePath(dir) catch unreachable; + // } + + var cacher = createCacheBuilder(self.builder); + for (self.resources.items) |res| { + cacher.addBytes(res.path); + try cacher.addFile(res.content); + } + + const root = try cacher.createAndGetDir(); + for (self.resources.items) |res| { + if (std.fs.path.dirname(res.path)) |folder| { + try root.dir.makePath(folder); + } + + const src_path = res.content.getPath(self.builder); + try std.fs.Dir.copyFile( + std.fs.cwd(), + src_path, + root.dir, + res.path, + .{}, + ); + } + + self.directory.path = root.path; + } +}; + +const CopyToZipStep = struct { + step: Step, + sdk: *Sdk, + target_dir: []const u8, + input_file: std.build.FileSource, + apk_file: []const u8, + + fn create(sdk: *Sdk, apk_file: []const u8, target_dir: []const u8, input_file: std.build.FileSource) *CopyToZipStep { + std.debug.assert(target_dir[target_dir.len - 1] == '/'); + const self = sdk.b.allocator.create(CopyToZipStep) catch unreachable; + self.* = CopyToZipStep{ + .step = Step.init(.custom, "copy to zip", sdk.b.allocator, make), + .target_dir = target_dir, + .input_file = input_file, + .sdk = sdk, + .apk_file = sdk.b.pathFromRoot(apk_file), + }; + self.step.dependOn(&sdk.host_tools.zip_add.step); + input_file.addStepDependencies(&self.step); + return self; + } + + // id: Id, name: []const u8, allocator: *Allocator, makeFn: fn (*Step) anyerror!void + + fn make(step: *Step) !void { + const self = @fieldParentPtr(CopyToZipStep, "step", step); + + const output_path = self.input_file.getPath(self.sdk.b); + + var zip_name = std.mem.concat(self.sdk.b.allocator, u8, &[_][]const u8{ + self.target_dir, + std.fs.path.basename(output_path), + }) catch unreachable; + + const args = [_][]const u8{ + self.sdk.host_tools.zip_add.getOutputSource().getPath(self.sdk.b), + self.apk_file, + output_path, + zip_name, + }; + + _ = try self.sdk.b.execFromStep(&args, &self.step); + } +}; + +/// Compiles a single .so file for the given platform. +/// Note that this function assumes your build script only uses a single `android_config`! +pub fn compileAppLibrary( + sdk: *const Sdk, + src_file: []const u8, + app_config: AppConfig, + mode: std.builtin.Mode, + target: Target, + // build_options: std.build.Pkg, +) *std.build.LibExeObjStep { + const ndk_root = sdk.b.pathFromRoot(sdk.folders.android_ndk_root); + + const exe = sdk.b.addSharedLibrary(app_config.app_name, src_file, .unversioned); + + exe.link_emit_relocs = true; + exe.link_eh_frame_hdr = true; + exe.force_pic = true; + exe.link_function_sections = true; + exe.bundle_compiler_rt = true; + exe.strip = (mode == .ReleaseSmall); + exe.export_table = true; + + exe.defineCMacro("ANDROID", null); + + exe.linkLibC(); + for (app_config.libraries) |lib| { + exe.linkSystemLibraryName(lib); + } + for (app_config.packages) |package| { + exe.addPackage(package); + } + + exe.setBuildMode(mode); + + const TargetConfig = struct { + lib_dir: []const u8, + include_dir: []const u8, + out_dir: []const u8, + target: std.zig.CrossTarget, + }; + + const config: TargetConfig = switch (target) { + .aarch64 => TargetConfig{ + .lib_dir = "aarch64-linux-android", + .include_dir = "aarch64-linux-android", + .out_dir = "arm64", + .target = zig_targets.aarch64, + }, + .arm => TargetConfig{ + .lib_dir = "arm-linux-androideabi", + .include_dir = "arm-linux-androideabi", + .out_dir = "armeabi", + .target = zig_targets.arm, + }, + .x86 => TargetConfig{ + .lib_dir = "i686-linux-android", + .include_dir = "i686-linux-android", + .out_dir = "x86", + .target = zig_targets.x86, + }, + .x86_64 => TargetConfig{ + .lib_dir = "x86_64-linux-android", + .include_dir = "x86_64-linux-android", + .out_dir = "x86_64", + .target = zig_targets.x86_64, + }, + }; + + const lib_dir = sdk.b.fmt("{s}/toolchains/llvm/prebuilt/{s}/sysroot/usr/lib/{s}/{d}/", .{ + ndk_root, + toolchainHostTag(), + config.lib_dir, + @enumToInt(app_config.target_version), + }); + + const include_dir = std.fs.path.resolve(sdk.b.allocator, &[_][]const u8{ + ndk_root, + "toolchains", + "llvm", + "prebuilt", + toolchainHostTag(), + "sysroot", + "usr", + "include", + }) catch unreachable; + const system_include_dir = std.fs.path.resolve(sdk.b.allocator, &[_][]const u8{ include_dir, config.include_dir }) catch unreachable; + + // exe.addIncludePath(include_dir); + + exe.setTarget(config.target); + exe.addLibraryPath(lib_dir); + + // exe.addIncludePath(include_dir); + // exe.addIncludePath(system_include_dir); + + exe.setLibCFile(sdk.createLibCFile(app_config.target_version, config.out_dir, include_dir, system_include_dir, lib_dir) catch unreachable); + exe.libc_file.?.addStepDependencies(&exe.step); + + exe.install(); + + // TODO: Remove when https://github.com/ziglang/zig/issues/7935 is resolved: + if (exe.target.getCpuArch() == .x86) { + exe.link_z_notext = true; + } + + return exe; +} + +fn createLibCFile(sdk: *const Sdk, version: AndroidVersion, folder_name: []const u8, include_dir: []const u8, sys_include_dir: []const u8, crt_dir: []const u8) !std.build.FileSource { + const fname = sdk.b.fmt("android-{d}-{s}.conf", .{ @enumToInt(version), folder_name }); + + var contents = std.ArrayList(u8).init(sdk.b.allocator); + errdefer contents.deinit(); + + var writer = contents.writer(); + + // The directory that contains `stdlib.h`. + // On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null + try writer.print("include_dir={s}\n", .{include_dir}); + + // The system-specific include directory. May be the same as `include_dir`. + // On Windows it's the directory that includes `vcruntime.h`. + // On POSIX it's the directory that includes `sys/errno.h`. + try writer.print("sys_include_dir={s}\n", .{sys_include_dir}); + + try writer.print("crt_dir={s}\n", .{crt_dir}); + try writer.writeAll("msvc_lib_dir=\n"); + try writer.writeAll("kernel32_lib_dir=\n"); + try writer.writeAll("gcc_dir=\n"); + + const step = sdk.b.addWriteFile(fname, contents.items); + return step.getFileSource(fname) orelse unreachable; +} + +pub fn compressApk(sdk: Sdk, input_apk_file: []const u8, output_apk_file: []const u8) *Step { + const temp_folder = sdk.b.pathFromRoot("zig-cache/apk-compress-folder"); + + const mkdir_cmd = sdk.b.addSystemCommand(&[_][]const u8{ + sdk.system_tools.mkdir, + temp_folder, + }); + + const unpack_apk = sdk.b.addSystemCommand(&[_][]const u8{ + "unzip", + "-o", + sdk.builder.pathFromRoot(input_apk_file), + "-d", + temp_folder, + }); + unpack_apk.step.dependOn(&mkdir_cmd.step); + + const repack_apk = sdk.b.addSystemCommand(&[_][]const u8{ + "zip", + "-D9r", + sdk.builder.pathFromRoot(output_apk_file), + ".", + }); + repack_apk.cwd = temp_folder; + repack_apk.step.dependOn(&unpack_apk.step); + + const rmdir_cmd = sdk.b.addSystemCommand(&[_][]const u8{ + sdk.system_tools.rm, + "-rf", + temp_folder, + }); + rmdir_cmd.step.dependOn(&repack_apk.step); + return &rmdir_cmd.step; +} + +pub fn signApk(sdk: Sdk, apk_file: []const u8, key_store: KeyStore) *Step { + const pass = sdk.b.fmt("pass:{s}", .{key_store.password}); + const sign_apk = sdk.b.addSystemCommand(&[_][]const u8{ + sdk.system_tools.apksigner, + "sign", + "--ks", // keystore + key_store.file, + "--ks-pass", + pass, + sdk.b.pathFromRoot(apk_file), + // key_store.alias, + }); + return &sign_apk.step; +} + +pub fn alignApk(sdk: Sdk, input_apk_file: []const u8, output_apk_file: []const u8) *Step { + const step = sdk.b.addSystemCommand(&[_][]const u8{ + sdk.system_tools.zipalign, + "-p", // ensure shared libraries are aligned to 4KiB + "-f", // overwrite existing files + "-v", // verbose + "4", + sdk.b.pathFromRoot(input_apk_file), + sdk.b.pathFromRoot(output_apk_file), + }); + return &step.step; +} + +pub fn installApp(sdk: Sdk, apk_file: std.build.FileSource) *Step { + const step = sdk.b.addSystemCommand(&[_][]const u8{ sdk.system_tools.adb, "install" }); + step.addFileSourceArg(apk_file); + return &step.step; +} + +pub fn startApp(sdk: Sdk, package_name: []const u8) *Step { + const step = sdk.b.addSystemCommand(&[_][]const u8{ + sdk.system_tools.adb, + "shell", + "am", + "start", + "-n", + sdk.b.fmt("{s}/android.app.NativeActivity", .{package_name}), + }); + return &step.step; +} + +/// Configuration for a signing key. +pub const KeyConfig = struct { + pub const Algorithm = enum { RSA }; + key_algorithm: Algorithm = .RSA, + key_size: u32 = 2048, // bits + validity: u32 = 10_000, // days + distinguished_name: []const u8 = "CN=example.com, OU=ID, O=Example, L=Doe, S=John, C=GB", +}; +/// A build step that initializes a new key store from the given configuration. +/// `android_config.key_store` must be non-`null` as it is used to initialize the key store. +pub fn initKeystore(sdk: Sdk, key_store: KeyStore, key_config: KeyConfig) *Step { + const step = sdk.b.addSystemCommand(&[_][]const u8{ + sdk.system_tools.keytool, + "-genkey", + "-v", + "-keystore", + key_store.file, + "-alias", + key_store.alias, + "-keyalg", + @tagName(key_config.key_algorithm), + "-keysize", + sdk.b.fmt("{d}", .{key_config.key_size}), + "-validity", + sdk.b.fmt("{d}", .{key_config.validity}), + "-storepass", + key_store.password, + "-keypass", + key_store.password, + "-dname", + key_config.distinguished_name, + }); + return &step.step; +} + +const Builder = std.build.Builder; +const Step = std.build.Step; + +const android_os = .linux; +const android_abi = .android; + +const zig_targets = struct { + const aarch64 = std.zig.CrossTarget{ + .cpu_arch = .aarch64, + .os_tag = android_os, + .abi = android_abi, + .cpu_model = .baseline, + .cpu_features_add = std.Target.aarch64.featureSet(&.{.v8a}), + }; + + const arm = std.zig.CrossTarget{ + .cpu_arch = .arm, + .os_tag = android_os, + .abi = android_abi, + .cpu_model = .baseline, + .cpu_features_add = std.Target.arm.featureSet(&.{.v7a}), + }; + + const x86 = std.zig.CrossTarget{ + .cpu_arch = .x86, + .os_tag = android_os, + .abi = android_abi, + .cpu_model = .baseline, + }; + + const x86_64 = std.zig.CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = android_os, + .abi = android_abi, + .cpu_model = .baseline, + }; +}; + +const app_libs = [_][]const u8{ + "GLESv2", "EGL", "android", "log", "aaudio" +}; + +const BuildOptionStep = struct { + const Self = @This(); + + step: Step, + builder: *std.build.Builder, + file_content: std.ArrayList(u8), + package_file: std.build.GeneratedFile, + + pub fn create(b: *Builder) *Self { + const options = b.allocator.create(Self) catch @panic("out of memory"); + + options.* = Self{ + .builder = b, + .step = Step.init(.custom, "render build options", b.allocator, make), + .file_content = std.ArrayList(u8).init(b.allocator), + .package_file = std.build.GeneratedFile{ .step = &options.step }, + }; + + return options; + } + + pub fn getPackage(self: *Self, name: []const u8) std.build.Pkg { + return self.builder.dupePkg(std.build.Pkg{ + .name = name, + .source = .{ .generated = &self.package_file }, + }); + } + + pub fn add(self: *Self, comptime T: type, name: []const u8, value: T) void { + const out = self.file_content.writer(); + switch (T) { + []const []const u8 => { + out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable; + for (value) |slice| { + out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable; + } + out.writeAll("};\n") catch unreachable; + return; + }, + [:0]const u8 => { + out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; + return; + }, + []const u8 => { + out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; + return; + }, + ?[:0]const u8 => { + out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; + if (value) |payload| { + out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; + } else { + out.writeAll("null;\n") catch unreachable; + } + return; + }, + ?[]const u8 => { + out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; + if (value) |payload| { + out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; + } else { + out.writeAll("null;\n") catch unreachable; + } + return; + }, + std.builtin.Version => { + out.print( + \\pub const {}: @import("std").builtin.Version = .{{ + \\ .major = {d}, + \\ .minor = {d}, + \\ .patch = {d}, + \\}}; + \\ + , .{ + std.zig.fmtId(name), + + value.major, + value.minor, + value.patch, + }) catch unreachable; + }, + std.SemanticVersion => { + out.print( + \\pub const {}: @import("std").SemanticVersion = .{{ + \\ .major = {d}, + \\ .minor = {d}, + \\ .patch = {d}, + \\ + , .{ + std.zig.fmtId(name), + + value.major, + value.minor, + value.patch, + }) catch unreachable; + if (value.pre) |some| { + out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; + } + if (value.build) |some| { + out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; + } + out.writeAll("};\n") catch unreachable; + return; + }, + else => {}, + } + switch (@typeInfo(T)) { + .Enum => |enum_info| { + out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable; + inline for (enum_info.fields) |field| { + out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable; + } + out.writeAll("};\n") catch unreachable; + }, + else => {}, + } + out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable; + } + + fn make(step: *Step) !void { + const self = @fieldParentPtr(Self, "step", step); + + var cacher = createCacheBuilder(self.builder); + cacher.addBytes(self.file_content.items); + + const root_path = try cacher.createAndGetPath(); + + self.package_file.path = try std.fs.path.join(self.builder.allocator, &[_][]const u8{ + root_path, + "build_options.zig", + }); + + try std.fs.cwd().writeFile(self.package_file.path.?, self.file_content.items); + } +}; + +fn createCacheBuilder(b: *std.build.Builder) CacheBuilder { + return CacheBuilder.init(b, "android-sdk"); +} + +const CacheBuilder = struct { + const Self = @This(); + + builder: *std.build.Builder, + hasher: std.crypto.hash.Sha1, + subdir: ?[]const u8, + + pub fn init(builder: *std.build.Builder, subdir: ?[]const u8) Self { + return Self{ + .builder = builder, + .hasher = std.crypto.hash.Sha1.init(.{}), + .subdir = if (subdir) |s| + builder.dupe(s) + else + null, + }; + } + + pub fn addBytes(self: *Self, bytes: []const u8) void { + self.hasher.update(bytes); + } + + pub fn addFile(self: *Self, file: std.build.FileSource) !void { + const path = file.getPath(self.builder); + + const data = try std.fs.cwd().readFileAlloc(self.builder.allocator, path, 1 << 32); // 4 GB + defer self.builder.allocator.free(data); + + self.addBytes(data); + } + + fn createPath(self: *Self) ![]const u8 { + var hash: [20]u8 = undefined; + self.hasher.final(&hash); + + const path = if (self.subdir) |subdir| + try std.fmt.allocPrint( + self.builder.allocator, + "{s}/{s}/o/{}", + .{ + self.builder.cache_root, + subdir, + std.fmt.fmtSliceHexLower(&hash), + }, + ) + else + try std.fmt.allocPrint( + self.builder.allocator, + "{s}/o/{}", + .{ + self.builder.cache_root, + std.fmt.fmtSliceHexLower(&hash), + }, + ); + + return path; + } + + pub const DirAndPath = struct { + dir: std.fs.Dir, + path: []const u8, + }; + pub fn createAndGetDir(self: *Self) !DirAndPath { + const path = try self.createPath(); + return DirAndPath{ + .path = path, + .dir = try std.fs.cwd().makeOpenPath(path, .{}), + }; + } + + pub fn createAndGetPath(self: *Self) ![]const u8 { + const path = try self.createPath(); + try std.fs.cwd().makePath(path); + return path; + } +}; + +/// A enumeration of all permissions. +/// See: https://developer.android.com/reference/android/Manifest.permission +pub const Permission = enum { + accept_handover, + access_background_location, + access_blobs_across_users, + access_checkin_properties, + access_coarse_location, + access_fine_location, + access_location_extra_commands, + access_media_location, + access_network_state, + access_notification_policy, + access_wifi_state, + account_manager, + activity_recognition, + add_voicemail, + answer_phone_calls, + battery_stats, + bind_accessibility_service, + bind_appwidget, + bind_autofill_service, + bind_call_redirection_service, + bind_carrier_messaging_client_service, + bind_carrier_messaging_service, + bind_carrier_services, + bind_chooser_target_service, + bind_companion_device_service, + bind_condition_provider_service, + bind_controls, + bind_device_admin, + bind_dream_service, + bind_incall_service, + bind_input_method, + bind_midi_device_service, + bind_nfc_service, + bind_notification_listener_service, + bind_print_service, + bind_quick_access_wallet_service, + bind_quick_settings_tile, + bind_remoteviews, + bind_screening_service, + bind_telecom_connection_service, + bind_text_service, + bind_tv_input, + bind_visual_voicemail_service, + bind_voice_interaction, + bind_vpn_service, + bind_vr_listener_service, + bind_wallpaper, + bluetooth, + bluetooth_admin, + bluetooth_advertise, + bluetooth_connect, + bluetooth_privileged, + bluetooth_scan, + body_sensors, + broadcast_package_removed, + broadcast_sms, + broadcast_sticky, + broadcast_wap_push, + call_companion_app, + call_phone, + call_privileged, + camera, + capture_audio_output, + change_component_enabled_state, + change_configuration, + change_network_state, + change_wifi_multicast_state, + change_wifi_state, + clear_app_cache, + control_location_updates, + delete_cache_files, + delete_packages, + diagnostic, + disable_keyguard, + dump, + expand_status_bar, + factory_test, + foreground_service, + get_accounts, + get_accounts_privileged, + get_package_size, + get_tasks, + global_search, + hide_overlay_windows, + high_sampling_rate_sensors, + install_location_provider, + install_packages, + install_shortcut, + instant_app_foreground_service, + interact_across_profiles, + internet, + kill_background_processes, + launch_two_pane_settings_deep_link, + loader_usage_stats, + location_hardware, + manage_documents, + manage_external_storage, + manage_media, + manage_ongoing_calls, + manage_own_calls, + master_clear, + media_content_control, + modify_audio_settings, + modify_phone_state, + mount_format_filesystems, + mount_unmount_filesystems, + nfc, + nfc_preferred_payment_info, + nfc_transaction_event, + package_usage_stats, + persistent_activity, + process_outgoing_calls, + query_all_packages, + read_calendar, + read_call_log, + read_contacts, + read_external_storage, + read_input_state, + read_logs, + read_phone_numbers, + read_phone_state, + read_precise_phone_state, + read_sms, + read_sync_settings, + read_sync_stats, + read_voicemail, + reboot, + receive_boot_completed, + receive_mms, + receive_sms, + receive_wap_push, + record_audio, + reorder_tasks, + request_companion_profile_watch, + request_companion_run_in_background, + request_companion_start_foreground_services_from_background, + request_companion_use_data_in_background, + request_delete_packages, + request_ignore_battery_optimizations, + request_install_packages, + request_observe_companion_device_presence, + request_password_complexity, + schedule_exact_alarm, + send_respond_via_message, + send_sms, + set_alarm, + set_always_finish, + set_animation_scale, + set_debug_app, + set_process_limit, + set_time, + set_time_zone, + set_wallpaper, + set_wallpaper_hints, + signal_persistent_processes, + sms_financial_transactions, + start_foreground_services_from_background, + start_view_permission_usage, + status_bar, + system_alert_window, + transmit_ir, + uninstall_shortcut, + update_device_stats, + update_packages_without_user_action, + use_biometric, + use_fingerprint, + use_full_screen_intent, + use_icc_auth_with_device_identifier, + use_sip, + uwb_ranging, + vibrate, + wake_lock, + write_apn_settings, + write_calendar, + write_call_log, + write_contacts, + write_external_storage, + write_gservices, + write_secure_settings, + write_settings, + write_sync_settings, + write_voicemail, + + pub fn toString(self: Permission) []const u8 { + @setEvalBranchQuota(10_000); + inline for (std.meta.fields(Permission)) |fld| { + if (self == @field(Permission, fld.name)) { + return comptime blk: { + var name: [fld.name.len]u8 = undefined; + break :blk "android.permission." ++ std.ascii.upperString(&name, fld.name); + }; + } + } + unreachable; + } +}; diff --git a/android/build.zig b/android/build.zig new file mode 100644 index 00000000..a613ebb5 --- /dev/null +++ b/android/build.zig @@ -0,0 +1,98 @@ +//! This is a example build.zig! +//! Use it as a template for your own projects, all generic build instructions +//! are contained in Sdk.zig. + +const std = @import("std"); +const Sdk = @import("Sdk.zig"); + +pub fn build(b: *std.build.Builder) !void { + // Default-initialize SDK + const sdk = Sdk.init(b, null, .{}); + const mode = b.standardReleaseOptions(); + const android_version = b.option(Sdk.AndroidVersion, "android", "Select the android version, default is 'android5'") orelse .android5; + const aaudio = b.option(bool, "aaudio", "Compile with support for AAudio, default is 'false'") orelse false; + const opensl = b.option(bool, "opensl", "Compile with support for OpenSL ES, default is 'true'") orelse true; + + // Provide some KeyStore structure so we can sign our app. + // Recommendation: Don't hardcore your password here, everyone can read it. + // At least not for your production keystore ;) + const key_store = Sdk.KeyStore{ + .file = ".build_config/android.keystore", + .alias = "default", + .password = "ziguana", + }; + + var libraries = std.ArrayList([]const u8).init(b.allocator); + try libraries.append("GLESv2"); + try libraries.append("EGL"); + try libraries.append("android"); + try libraries.append("log"); + + if (opensl) try libraries.append("OpenSLES"); + if (aaudio) try libraries.append("aaudio"); + + // This is a configuration for your application. + // Android requires several configurations to be done, this is a typical config + const config = Sdk.AppConfig{ + .target_version = android_version, + + // This is displayed to the user + .display_name = "Zig Android App Template", + + // This is used internally for ... things? + .app_name = "zig-app-template", + + // This is required for the APK name. This identifies your app, android will associate + // your signing key with this identifier and will prevent updates if the key changes. + .package_name = "net.random_projects.zig_android_template", + + // This is a set of resources. It should at least contain a "mipmap/icon.png" resource that + // will provide the application icon. + .resources = &[_]Sdk.Resource{ + .{ .path = "mipmap/icon.png", .content = .{ .path = "example/icon.png" } }, + }, + + .aaudio = aaudio, + + .opensl = opensl, + + // This is a list of android permissions. Check out the documentation to figure out which you need. + .permissions = &[_][]const u8{ + "android.permission.SET_RELEASE_APP", + //"android.permission.RECORD_AUDIO", + }, + + // This is a list of native android apis to link against. + .libraries = libraries.items, + }; + + const app = sdk.createApp( + "app-template.apk", + "example/main.zig", + config, + mode, + .{ + .aarch64 = b.option(bool, "aarch64", "Enable the aarch64 build"), + .arm = b.option(bool, "arm", "Enable the arm build"), + .x86_64 = b.option(bool, "x86_64", "Enable the x86_64 build"), + .x86 = b.option(bool, "x86", "Enable the x86 build"), + }, // default targets + key_store, + ); + + for (app.libraries) |exe| { + // Provide the "android" package in each executable we build + exe.addPackage(app.getAndroidPackage("android")); + } + + // Make the app build when we invoke "zig build" or "zig build install" + b.getInstallStep().dependOn(app.final_step); + + const keystore_step = b.step("keystore", "Initialize a fresh debug keystore"); + const push_step = b.step("push", "Push the app to a connected android device"); + const run_step = b.step("run", "Run the app on a connected android device"); + + keystore_step.dependOn(sdk.initKeystore(key_store, .{})); + push_step.dependOn(app.install()); + run_step.dependOn(app.run()); +} diff --git a/android/build/auto-detect.zig b/android/build/auto-detect.zig new file mode 100644 index 00000000..97f1a4fa --- /dev/null +++ b/android/build/auto-detect.zig @@ -0,0 +1,495 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Builder = std.build.Builder; + +const Sdk = @import("../Sdk.zig"); +const UserConfig = Sdk.UserConfig; + +// This config stores tool paths for the current machine +const build_config_dir = ".build_config"; +const local_config_file = "android.json"; + +const print = std.debug.print; + +pub fn findUserConfig(b: *Builder, versions: Sdk.ToolchainVersions) !UserConfig { + // var str_buf: [5]u8 = undefined; + + var config = UserConfig{}; + var config_dirty: bool = false; + + const local_config_path = pathConcat(b, build_config_dir, local_config_file); + const config_path = b.pathFromRoot(local_config_path); + const config_dir = b.pathFromRoot(build_config_dir); + + // Check for a user config file. + if (std.fs.cwd().openFile(config_path, .{})) |file| { + defer file.close(); + const bytes = file.readToEndAlloc(b.allocator, 1 * 1000 * 1000) catch |err| { + print("Unexpected error reading {s}: {s}\n", .{ config_path, @errorName(err) }); + return err; + }; + var stream = std.json.TokenStream.init(bytes); + if (std.json.parse(UserConfig, &stream, .{ .allocator = b.allocator })) |conf| { + config = conf; + } else |err| { + print("Could not parse {s} ({s}).\n", .{ config_path, @errorName(err) }); + return err; + } + } else |err| switch (err) { + error.FileNotFound => { + config_dirty = true; + }, + else => { + print("Unexpected error opening {s}: {s}\n", .{ config_path, @errorName(err) }); + return err; + }, + } + + // Verify the user config and set new values if needed + // First the android home + if (config.android_sdk_root.len > 0) { + if (findProblemWithAndroidSdk(b, versions, config.android_sdk_root)) |problem| { + print("Invalid android root directory: {s}\n {s}\n Looking for a new one.\n", .{ config.android_sdk_root, problem }); + config.android_sdk_root = ""; + // N.B. Don't dirty the file for this. We don't want to nuke the file if we can't find a replacement. + } + } + + if (config.android_sdk_root.len == 0) { + // try to find the android home + if (std.process.getEnvVarOwned(b.allocator, "ANDROID_HOME")) |value| { + if (value.len > 0) { + if (findProblemWithAndroidSdk(b, versions, value)) |problem| { + print("Cannot use ANDROID_HOME ({s}):\n {s}\n", .{ value, problem }); + } else { + print("Using android sdk at ANDROID_HOME: {s}\n", .{value}); + config.android_sdk_root = value; + config_dirty = true; + } + } + } else |_| { + // ignore if env var is not found + } + } + + if (config.android_sdk_root.len == 0) { + // try to find the android home + if (std.process.getEnvVarOwned(b.allocator, "ANDROID_SDK_ROOT")) |value| { + if (value.len > 0) { + if (findProblemWithAndroidSdk(b, versions, value)) |problem| { + print("Cannot use ANDROID_SDK_ROOT ({s}):\n {s}\n", .{ value, problem }); + } else { + print("Using android sdk at ANDROID_SDK_ROOT: {s}\n", .{value}); + config.android_sdk_root = value; + config_dirty = true; + } + } + } else |_| { + // ignore environment variable failure + } + } + + var android_studio_path: []const u8 = ""; + + // On windows, check for an android studio install. + // If it's present, it may have the sdk path stored in the registry. + // If not, check the default install location. + if (builtin.os.tag == .windows) { + const HKEY = ?*opaque {}; + const LSTATUS = u32; + const DWORD = u32; + + // const HKEY_CLASSES_ROOT = @intToPtr(HKEY, 0x80000000); + const HKEY_CURRENT_USER = @intToPtr(HKEY, 0x80000001); + const HKEY_LOCAL_MACHINE = @intToPtr(HKEY, 0x80000002); + // const HKEY_USERS = @intToPtr(HKEY, 0x80000003); + + // const RRF_RT_ANY: DWORD = 0xFFFF; + // const RRF_RT_REG_BINARY: DWORD = 0x08; + // const RRF_RT_REG_DWORD: DWORD = 0x10; + // const RRF_RT_REG_EXPAND_SZ: DWORD = 0x04; + // const RRF_RT_REG_MULTI_SZ: DWORD = 0x20; + // const RRF_RT_REG_NONE: DWORD = 0x01; + // const RRF_RT_REG_QWORD: DWORD = 0x40; + const RRF_RT_REG_SZ: DWORD = 0x02; + // const RRF_RT_DWORD = RRF_RT_REG_DWORD | RRF_RT_REG_BINARY; + // const RRF_RT_QWORD = RRF_RT_REG_QWORD | RRF_RT_REG_BINARY; + + // const RRF_NOEXPAND: DWORD = 0x10000000; + // const RRF_ZEROONFAILURE: DWORD = 0x20000000; + // const RRF_SUBKEY_WOW6464KEY: DWORD = 0x00010000; + // const RRF_SUBKEY_WOW6432KEY: DWORD = 0x00020000; + + const ERROR_SUCCESS: LSTATUS = 0; + const ERROR_MORE_DATA: LSTATUS = 234; + + const reg = struct { + extern "Advapi32" fn RegOpenKeyA(key: HKEY, subKey: [*:0]const u8, result: *HKEY) LSTATUS; + extern "Advapi32" fn RegCloseKey(key: HKEY) LSTATUS; + extern "Advapi32" fn RegGetValueA(key: HKEY, subKey: ?[*:0]const u8, value: [*:0]const u8, flags: DWORD, type: ?*DWORD, data: ?*anyopaque, len: ?*DWORD) LSTATUS; + + fn getStringAlloc(allocator: std.mem.Allocator, key: HKEY, value: [*:0]const u8) ?[]const u8 { + // query the length + var len: DWORD = 0; + var res = RegGetValueA(key, null, value, RRF_RT_REG_SZ, null, null, &len); + if (res == ERROR_SUCCESS) { + if (len == 0) { + return &[_]u8{}; + } + } else if (res != ERROR_MORE_DATA) { + return null; + } + + // get the data + const buffer = allocator.alloc(u8, len) catch unreachable; + len = @intCast(DWORD, buffer.len); + res = RegGetValueA(key, null, value, RRF_RT_REG_SZ, null, buffer.ptr, &len); + if (res == ERROR_SUCCESS) { + for (buffer[0..len]) |c, i| { + if (c == 0) return buffer[0..i]; + } + return buffer[0..len]; + } + allocator.free(buffer); + return null; + } + }; + + // Get the android studio registry entry + var android_studio_key: HKEY = for ([_]HKEY{ HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }) |root_key| { + var software: HKEY = null; + if (reg.RegOpenKeyA(root_key, "software", &software) == ERROR_SUCCESS) { + defer _ = reg.RegCloseKey(software); + var android: HKEY = null; + if (reg.RegOpenKeyA(software, "Android Studio", &android) == ERROR_SUCCESS) { + if (android != null) break android; + } + } + } else null; + + // Grab the paths to the android studio install and the sdk install. + if (android_studio_key != null) { + defer _ = reg.RegCloseKey(android_studio_key); + if (reg.getStringAlloc(b.allocator, android_studio_key, "Path")) |path| { + android_studio_path = path; + } else { + print("Could not get android studio path\n", .{}); + } + if (reg.getStringAlloc(b.allocator, android_studio_key, "SdkPath")) |sdk_path| { + if (sdk_path.len > 0) { + if (findProblemWithAndroidSdk(b, versions, sdk_path)) |problem| { + print("Cannot use Android Studio sdk ({s}):\n {s}\n", .{ sdk_path, problem }); + } else { + print("Using android sdk from Android Studio: {s}\n", .{sdk_path}); + config.android_sdk_root = sdk_path; + config_dirty = true; + } + } + } + } + + // If we didn't find an sdk in the registry, check the default install location. + // On windows, this is AppData/Local/Android. + if (config.android_sdk_root.len == 0) { + if (std.process.getEnvVarOwned(b.allocator, "LOCALAPPDATA")) |appdata_local| { + const sdk_path = pathConcat(b, appdata_local, "Android"); + if (findProblemWithAndroidSdk(b, versions, sdk_path)) |problem| { + print("Cannot use default Android Studio SDK\n at {s}:\n {s}\n", .{ sdk_path, problem }); + } else { + print("Using android sdk from Android Studio: {s}\n", .{sdk_path}); + config.android_sdk_root = sdk_path; + config_dirty = true; + } + } else |_| { + // ignore env + } + } + } + + // Finally, if we still don't have an sdk, see if `adb` is on the path and try to use that. + if (config.android_sdk_root.len == 0) { + if (findProgramPath(b.allocator, "adb")) |path| { + const sep = std.fs.path.sep; + if (std.mem.lastIndexOfScalar(u8, path, sep)) |index| { + var rest = path[0..index]; + const parent = "platform-tools"; + if (std.mem.endsWith(u8, rest, parent) and rest[rest.len - parent.len - 1] == sep) { + const sdk_path = rest[0 .. rest.len - parent.len - 1]; + if (findProblemWithAndroidSdk(b, versions, sdk_path)) |problem| { + print("Cannot use SDK near adb\n at {s}:\n {s}\n", .{ sdk_path, problem }); + } else { + print("Using android sdk near adb: {s}\n", .{sdk_path}); + config.android_sdk_root = sdk_path; + config_dirty = true; + } + } + } + } + } + + // Next up, NDK. + if (config.android_ndk_root.len > 0) { + if (findProblemWithAndroidNdk(b, versions, config.android_ndk_root)) |problem| { + print("Saved NDK is invalid ({s})\n{s}\n", .{ config.android_ndk_root, problem }); + config.android_ndk_root = ""; + } + } + + // first, check ANDROID_NDK_ROOT + if (config.android_ndk_root.len == 0) { + if (std.process.getEnvVarOwned(b.allocator, "ANDROID_NDK_ROOT")) |value| { + if (value.len > 0) { + if (findProblemWithAndroidNdk(b, versions, value)) |problem| { + print("Cannot use ANDROID_NDK_ROOT ({s}):\n {s}\n", .{ value, problem }); + } else { + print("Using android ndk at ANDROID_NDK_ROOT: {s}\n", .{value}); + config.android_ndk_root = value; + config_dirty = true; + } + } + } else |_| {} + } + + // Then check for a side-by-side install + if (config.android_ndk_root.len == 0) { + if (config.android_sdk_root.len > 0) { + const ndk_root = std.fs.path.join(b.allocator, &[_][]const u8{ + config.android_sdk_root, + "ndk", + versions.ndk_version, + }) catch unreachable; + if (findProblemWithAndroidNdk(b, versions, ndk_root)) |problem| { + print("Cannot use side by side NDK ({s}):\n {s}\n", .{ ndk_root, problem }); + } else { + print("Using side by side NDK install: {s}\n", .{ndk_root}); + config.android_ndk_root = ndk_root; + config_dirty = true; + } + } + } + + // Finally, we need to find the JDK, for jarsigner. + if (config.java_home.len > 0) { + if (findProblemWithJdk(b, config.java_home)) |problem| { + print("Cannot use configured java install {s}: {s}\n", .{ config.java_home, problem }); + config.java_home = ""; + } + } + + // Check the JAVA_HOME variable + if (config.java_home.len == 0) { + if (std.process.getEnvVarOwned(b.allocator, "JAVA_HOME")) |value| { + if (value.len > 0) { + if (findProblemWithJdk(b, value)) |problem| { + print("Cannot use JAVA_HOME ({s}):\n {s}\n", .{ value, problem }); + } else { + print("Using java JAVA_HOME: {s}\n", .{value}); + config.java_home = value; + config_dirty = true; + } + } + } else |_| {} + } + + // Look for `where jarsigner` + if (config.java_home.len == 0) { + if (findProgramPath(b.allocator, "jarsigner")) |path| { + const sep = std.fs.path.sep; + if (std.mem.lastIndexOfScalar(u8, path, sep)) |last_slash| { + if (std.mem.lastIndexOfScalar(u8, path[0..last_slash], sep)) |second_slash| { + const home = path[0..second_slash]; + if (findProblemWithJdk(b, home)) |problem| { + print("Cannot use java at ({s}):\n {s}\n", .{ home, problem }); + } else { + print("Using java at {s}\n", .{home}); + config.java_home = home; + config_dirty = true; + } + } + } + } + } + + // If we have Android Studio installed, it packages a JDK. + // Check for that. + if (config.java_home.len == 0) { + if (android_studio_path.len > 0) { + const packaged_jre = pathConcat(b, android_studio_path, "jre"); + if (findProblemWithJdk(b, packaged_jre)) |problem| { + print("Cannot use Android Studio java at ({s}):\n {s}\n", .{ packaged_jre, problem }); + } else { + print("Using java from Android Studio: {s}\n", .{packaged_jre}); + config.java_home = packaged_jre; + config_dirty = true; + } + } + } + + // Write out the new config + if (config_dirty) { + std.fs.cwd().makeDir(config_dir) catch {}; + var file = std.fs.cwd().createFile(config_path, .{}) catch |err| { + print("Couldn't write config file {s}: {s}\n\n", .{ config_path, @errorName(err) }); + return err; + }; + defer file.close(); + + var buf_writer = std.io.bufferedWriter(file.writer()); + + std.json.stringify(config, .{}, buf_writer.writer()) catch |err| { + print("Error writing config file {s}: {s}\n", .{ config_path, @errorName(err) }); + return err; + }; + buf_writer.flush() catch |err| { + print("Error writing config file {s}: {s}\n", .{ config_path, @errorName(err) }); + return err; + }; + } + + // Check if the config is invalid. + if (config.android_sdk_root.len == 0 or + config.android_ndk_root.len == 0 or + config.java_home.len == 0) + { + print("\nCould not find all needed tools. Please edit {s} to specify their paths.\n\n", .{local_config_path}); + if (config.android_sdk_root.len == 0) { + print("Android SDK root is missing. Edit the config file, or set ANDROID_SDK_ROOT to your android install.\n", .{}); + print("You will need build tools version {s} and android sdk platform {s}\n\n", .{ versions.build_tools_version, "TODO: ???" }); + } + if (config.android_ndk_root.len == 0) { + print("Android NDK root is missing. Edit the config file, or set ANDROID_NDK_ROOT to your android NDK install.\n", .{}); + print("You will need NDK version {s}\n\n", .{versions.ndk_version}); + } + if (config.java_home.len == 0) { + print("Java JDK is missing. Edit the config file, or set JAVA_HOME to your JDK install.\n", .{}); + if (builtin.os.tag == .windows) { + print("Installing Android Studio will also install a suitable JDK.\n", .{}); + } + print("\n", .{}); + } + + std.os.exit(1); + } + + if (config_dirty) { + print("New configuration:\nSDK: {s}\nNDK: {s}\nJDK: {s}\n", .{ config.android_sdk_root, config.android_ndk_root, config.java_home }); + } + + return config; +} + +fn findProgramPath(allocator: std.mem.Allocator, program: []const u8) ?[]const u8 { + const args: []const []const u8 = if (builtin.os.tag == .windows) + &[_][]const u8{ "where", program } + else + &[_][]const u8{ "which", program }; + + var proc = std.ChildProcess.init(args, allocator); + + proc.stderr_behavior = .Close; + proc.stdout_behavior = .Pipe; + proc.stdin_behavior = .Close; + + proc.spawn() catch return null; + + const stdout = proc.stdout.?.readToEndAlloc(allocator, 1024) catch return null; + const term = proc.wait() catch return null; + switch (term) { + .Exited => |rc| { + if (rc != 0) return null; + }, + else => return null, + } + + var path = std.mem.trim(u8, stdout, " \t\r\n"); + if (std.mem.indexOfScalar(u8, path, '\n')) |index| { + path = std.mem.trim(u8, path[0..index], " \t\r\n"); + } + if (path.len > 0) return path; + + return null; +} + +// Returns the problem with an android_home path. +// If it seems alright, returns null. +fn findProblemWithAndroidSdk(b: *Builder, versions: Sdk.ToolchainVersions, path: []const u8) ?[]const u8 { + std.fs.cwd().access(path, .{}) catch |err| { + if (err == error.FileNotFound) return "Directory does not exist"; + return b.fmt("Cannot access {s}, {s}", .{ path, @errorName(err) }); + }; + + const build_tools = pathConcat(b, path, "build-tools"); + std.fs.cwd().access(build_tools, .{}) catch |err| { + return b.fmt("Cannot access build-tools/, {s}", .{@errorName(err)}); + }; + + const versioned_tools = pathConcat(b, build_tools, versions.build_tools_version); + std.fs.cwd().access(versioned_tools, .{}) catch |err| { + if (err == error.FileNotFound) { + return b.fmt("Missing build tools version {s}", .{versions.build_tools_version}); + } else { + return b.fmt("Cannot access build-tools/{s}/, {s}", .{ versions.build_tools_version, @errorName(err) }); + } + }; + + // var str_buf: [5]u8 = undefined; + // const android_version_str = "TODO: ???"; // versions.androidSdkString(&str_buf); + + // const platforms = pathConcat(b, path, "platforms"); + // const platform_version = pathConcat(b, platforms, b.fmt("android-{d}", .{versions.android_sdk_version})); + // std.fs.cwd().access(platform_version, .{}) catch |err| { + // if (err == error.FileNotFound) { + // return b.fmt("Missing android platform version {s}", .{android_version_str}); + // } else { + // return b.fmt("Cannot access platforms/android-{s}, {s}", .{ android_version_str, @errorName(err) }); + // } + // }; + + return null; +} + +// Returns the problem with an android ndk path. +// If it seems alright, returns null. +fn findProblemWithAndroidNdk(b: *Builder, versions: Sdk.ToolchainVersions, path: []const u8) ?[]const u8 { + std.fs.cwd().access(path, .{}) catch |err| { + if (err == error.FileNotFound) return "Directory does not exist"; + return b.fmt("Cannot access {s}, {s}", .{ path, @errorName(err) }); + }; + + const ndk_include_path = std.fs.path.join(b.allocator, &[_][]const u8{ + path, + "toolchains", + "llvm", + "prebuilt", + Sdk.toolchainHostTag(), + "sysroot", + "usr", + "include", + }) catch unreachable; + std.fs.cwd().access(ndk_include_path, .{}) catch |err| { + return b.fmt("Cannot access {s}, {s}\nMake sure you are using NDK {s}.", .{ ndk_include_path, @errorName(err), versions.ndk_version }); + }; + + return null; +} + +// Returns the problem with a jdk install. +// If it seems alright, returns null. +fn findProblemWithJdk(b: *Builder, path: []const u8) ?[]const u8 { + std.fs.cwd().access(path, .{}) catch |err| { + if (err == error.FileNotFound) return "Directory does not exist"; + return b.fmt("Cannot access {s}, {s}", .{ path, @errorName(err) }); + }; + + const target_executable = if (builtin.os.tag == .windows) "bin\\jarsigner.exe" else "bin/jarsigner"; + const target_path = pathConcat(b, path, target_executable); + std.fs.cwd().access(target_path, .{}) catch |err| { + return b.fmt("Cannot access jarsigner, {s}", .{@errorName(err)}); + }; + + return null; +} + +fn pathConcat(b: *Builder, left: []const u8, right: []const u8) []const u8 { + return std.fs.path.join(b.allocator, &[_][]const u8{ left, right }) catch unreachable; +} diff --git a/android/example/icon.png b/android/example/icon.png new file mode 100644 index 00000000..7ea4bd87 Binary files /dev/null and b/android/example/icon.png differ diff --git a/android/example/logo.stl b/android/example/logo.stl new file mode 100644 index 00000000..851ab482 Binary files /dev/null and b/android/example/logo.stl differ diff --git a/android/example/main.zig b/android/example/main.zig new file mode 100644 index 00000000..c2f2ded9 --- /dev/null +++ b/android/example/main.zig @@ -0,0 +1,904 @@ +const std = @import("std"); + +const android = @import("android"); + +const audio = android.audio; +pub const panic = android.panic; +pub const log = android.log; + +const EGLContext = android.egl.EGLContext; +const JNI = android.JNI; +const c = android.egl.c; + +const app_log = std.log.scoped(.app); + +comptime { + _ = android.ANativeActivity_createFunc; +} + +/// Entry point for our application. +/// This struct provides the interface to the android support package. +pub const AndroidApp = struct { + const Self = @This(); + + const TouchPoint = struct { + /// if null, then fade out + index: ?i32, + intensity: f32, + x: f32, + y: f32, + age: i64, + }; + + allocator: std.mem.Allocator, + activity: *android.ANativeActivity, + + thread: ?std.Thread = null, + running: bool = true, + + egl_lock: std.Thread.Mutex = .{}, + egl: ?EGLContext = null, + egl_init: bool = true, + + input_lock: std.Thread.Mutex = .{}, + input: ?*android.AInputQueue = null, + + config: ?*android.AConfiguration = null, + + touch_points: [16]?TouchPoint = [1]?TouchPoint{null} ** 16, + screen_width: f32 = undefined, + screen_height: f32 = undefined, + + // audio_engine: audio.AudioEngine = .{}, + simple_synth: SimpleSynth = undefined, + + /// This is the entry point which initializes a application + /// that has stored its previous state. + /// `stored_state` is that state, the memory is only valid for this function. + pub fn init(allocator: std.mem.Allocator, activity: *android.ANativeActivity, stored_state: ?[]const u8) !Self { + _ = stored_state; + + return Self{ + .allocator = allocator, + .activity = activity, + }; + } + + /// This function is called when the application is successfully initialized. + /// It should create a background thread that processes the events and runs until + /// the application gets destroyed. + pub fn start(self: *Self) !void { + self.thread = try std.Thread.spawn(.{}, mainLoop, .{self}); + } + + /// Uninitialize the application. + /// Don't forget to stop your background thread here! + pub fn deinit(self: *Self) void { + @atomicStore(bool, &self.running, false, .SeqCst); + if (self.thread) |thread| { + thread.join(); + self.thread = null; + } + if (self.config) |config| { + android.AConfiguration_delete(config); + } + self.* = undefined; + } + + pub fn onNativeWindowCreated(self: *Self, window: *android.ANativeWindow) void { + self.egl_lock.lock(); + defer self.egl_lock.unlock(); + + if (self.egl) |*old| { + old.deinit(); + } + + self.screen_width = @intToFloat(f32, android.ANativeWindow_getWidth(window)); + self.screen_height = @intToFloat(f32, android.ANativeWindow_getHeight(window)); + + self.egl = EGLContext.init(window, .gles2) catch |err| blk: { + app_log.err("Failed to initialize EGL for window: {}\n", .{err}); + break :blk null; + }; + self.egl_init = true; + } + + pub fn onNativeWindowDestroyed(self: *Self, window: *android.ANativeWindow) void { + _ = window; + self.egl_lock.lock(); + defer self.egl_lock.unlock(); + + if (self.egl) |*old| { + old.deinit(); + } + self.egl = null; + } + + pub fn onInputQueueCreated(self: *Self, input: *android.AInputQueue) void { + self.input_lock.lock(); + defer self.input_lock.unlock(); + + self.input = input; + } + + pub fn onInputQueueDestroyed(self: *Self, input: *android.AInputQueue) void { + _ = input; + + self.input_lock.lock(); + defer self.input_lock.unlock(); + + self.input = null; + } + + fn printConfig(config: *android.AConfiguration) void { + var lang: [2]u8 = undefined; + var country: [2]u8 = undefined; + + android.AConfiguration_getLanguage(config, &lang); + android.AConfiguration_getCountry(config, &country); + + app_log.debug( + \\App Configuration: + \\ MCC: {} + \\ MNC: {} + \\ Language: {s} + \\ Country: {s} + \\ Orientation: {} + \\ Touchscreen: {} + \\ Density: {} + \\ Keyboard: {} + \\ Navigation: {} + \\ KeysHidden: {} + \\ NavHidden: {} + \\ SdkVersion: {} + \\ ScreenSize: {} + \\ ScreenLong: {} + \\ UiModeType: {} + \\ UiModeNight: {} + \\ + , .{ + android.AConfiguration_getMcc(config), + android.AConfiguration_getMnc(config), + &lang, + &country, + android.AConfiguration_getOrientation(config), + android.AConfiguration_getTouchscreen(config), + android.AConfiguration_getDensity(config), + android.AConfiguration_getKeyboard(config), + android.AConfiguration_getNavigation(config), + android.AConfiguration_getKeysHidden(config), + android.AConfiguration_getNavHidden(config), + android.AConfiguration_getSdkVersion(config), + android.AConfiguration_getScreenSize(config), + android.AConfiguration_getScreenLong(config), + android.AConfiguration_getUiModeType(config), + android.AConfiguration_getUiModeNight(config), + }); + } + + fn processKeyEvent(self: *Self, event: *android.AInputEvent) !bool { + const event_type = @intToEnum(android.AKeyEventActionType, android.AKeyEvent_getAction(event)); + std.log.scoped(.input).debug( + \\Key Press Event: {} + \\ Flags: {} + \\ KeyCode: {} + \\ ScanCode: {} + \\ MetaState: {} + \\ RepeatCount: {} + \\ DownTime: {} + \\ EventTime: {} + \\ + , .{ + event_type, + android.AKeyEvent_getFlags(event), + android.AKeyEvent_getKeyCode(event), + android.AKeyEvent_getScanCode(event), + android.AKeyEvent_getMetaState(event), + android.AKeyEvent_getRepeatCount(event), + android.AKeyEvent_getDownTime(event), + android.AKeyEvent_getEventTime(event), + }); + + if (event_type == .AKEY_EVENT_ACTION_DOWN) { + var jni = JNI.init(self.activity); + defer jni.deinit(); + + var codepoint = jni.AndroidGetUnicodeChar( + android.AKeyEvent_getKeyCode(event), + android.AKeyEvent_getMetaState(event), + ); + var buf: [8]u8 = undefined; + + var len = std.unicode.utf8Encode(codepoint, &buf) catch 0; + var key_text = buf[0..len]; + + std.log.scoped(.input).info("Pressed key: '{s}' U+{X}", .{ key_text, codepoint }); + } + + return false; + } + + fn insertPoint(self: *Self, point: TouchPoint) void { + std.debug.assert(point.index != null); + var oldest: *TouchPoint = undefined; + + if (point.index) |index| { + self.simple_synth.oscillators[@intCast(usize, index)].setWaveOn(true); + } + + for (self.touch_points) |*opt, i| { + if (opt.*) |*pt| { + if (pt.index != null and pt.index.? == point.index.?) { + pt.* = point; + return; + } + + if (i == 0) { + oldest = pt; + } else { + if (pt.age < oldest.age) { + oldest = pt; + } + } + } else { + opt.* = point; + return; + } + } + oldest.* = point; + } + + fn processMotionEvent(self: *Self, event: *android.AInputEvent) !bool { + const event_type = @intToEnum(android.AMotionEventActionType, android.AMotionEvent_getAction(event)); + + { + var jni = JNI.init(self.activity); + defer jni.deinit(); + + // Show/Hide keyboard + // _ = jni.AndroidDisplayKeyboard(true); + + // this allows you to send the app in the background + // const success = jni.AndroidSendToBack(true); + // _ = success; + // std.log.scoped(.input).debug("SendToBack() = {}\n", .{success}); + + // This is a demo on how to request permissions: + if (event_type == .AMOTION_EVENT_ACTION_UP) { + if (!JNI.AndroidHasPermissions(&jni, "android.permission.RECORD_AUDIO")) { + JNI.AndroidRequestAppPermissions(&jni, "android.permission.RECORD_AUDIO"); + } + } + } + + std.log.scoped(.input).debug( + \\Motion Event {} + \\ Flags: {} + \\ MetaState: {} + \\ ButtonState: {} + \\ EdgeFlags: {} + \\ DownTime: {} + \\ EventTime: {} + \\ XOffset: {} + \\ YOffset: {} + \\ XPrecision: {} + \\ YPrecision: {} + \\ PointerCount: {} + \\ + , .{ + event_type, + android.AMotionEvent_getFlags(event), + android.AMotionEvent_getMetaState(event), + android.AMotionEvent_getButtonState(event), + android.AMotionEvent_getEdgeFlags(event), + android.AMotionEvent_getDownTime(event), + android.AMotionEvent_getEventTime(event), + android.AMotionEvent_getXOffset(event), + android.AMotionEvent_getYOffset(event), + android.AMotionEvent_getXPrecision(event), + android.AMotionEvent_getYPrecision(event), + android.AMotionEvent_getPointerCount(event), + }); + + var i: usize = 0; + var cnt = android.AMotionEvent_getPointerCount(event); + while (i < cnt) : (i += 1) { + std.log.scoped(.input).debug( + \\Pointer {}: + \\ PointerId: {} + \\ ToolType: {} + \\ RawX: {d} + \\ RawY: {d} + \\ X: {d} + \\ Y: {d} + \\ Pressure: {} + \\ Size: {} + \\ TouchMajor: {} + \\ TouchMinor: {} + \\ ToolMajor: {} + \\ ToolMinor: {} + \\ Orientation: {} + \\ + , .{ + i, + android.AMotionEvent_getPointerId(event, i), + android.AMotionEvent_getToolType(event, i), + android.AMotionEvent_getRawX(event, i), + android.AMotionEvent_getRawY(event, i), + android.AMotionEvent_getX(event, i), + android.AMotionEvent_getY(event, i), + android.AMotionEvent_getPressure(event, i), + android.AMotionEvent_getSize(event, i), + android.AMotionEvent_getTouchMajor(event, i), + android.AMotionEvent_getTouchMinor(event, i), + android.AMotionEvent_getToolMajor(event, i), + android.AMotionEvent_getToolMinor(event, i), + android.AMotionEvent_getOrientation(event, i), + }); + + self.insertPoint(TouchPoint{ + .x = android.AMotionEvent_getX(event, i), + .y = android.AMotionEvent_getY(event, i), + .index = android.AMotionEvent_getPointerId(event, i), + .age = android.AMotionEvent_getEventTime(event), + .intensity = 1.0, + }); + } + + return false; + } + + fn mainLoop(self: *Self) !void { + // This code somehow crashes yet. Needs more investigations + var jni = JNI.init(self.activity); + defer jni.deinit(); + + // Must be called from main thread… + _ = jni.AndroidMakeFullscreen(); + + var loop: usize = 0; + app_log.info("mainLoop() started\n", .{}); + + self.config = blk: { + var cfg = android.AConfiguration_new() orelse return error.OutOfMemory; + android.AConfiguration_fromAssetManager(cfg, self.activity.assetManager); + break :blk cfg; + }; + + if (self.config) |cfg| { + printConfig(cfg); + } + + // Audio + self.simple_synth = SimpleSynth.init(); + + try audio.init(); + + var output_stream = try audio.getOutputStream(self.allocator, .{ + .sample_format = .Int16, + .callback = SimpleSynth.audioCallback, + .user_data = &self.simple_synth, + }); + defer { + output_stream.stop(); + output_stream.deinit(); + } + + try output_stream.start(); + + // Graphics + const GLuint = c.GLuint; + + var touch_program: GLuint = undefined; + var shaded_program: GLuint = undefined; + + var uPos: c.GLint = undefined; + var uAspect: c.GLint = undefined; + var uIntensity: c.GLint = undefined; + + var vPosition: c.GLuint = undefined; + + var uTransform: c.GLint = undefined; + + var mesh_vPosition: c.GLuint = undefined; + var mesh_vNormal: c.GLuint = undefined; + + var touch_buffer: c.GLuint = undefined; + var mesh_buffer: c.GLuint = undefined; + + const vVertices = [_]c.GLfloat{ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + }; + + while (@atomicLoad(bool, &self.running, .SeqCst)) { + + // Input process + { + // we lock the handle of our input so we don't have a race condition + self.input_lock.lock(); + defer self.input_lock.unlock(); + if (self.input) |input| { + var event: ?*android.AInputEvent = undefined; + while (android.AInputQueue_getEvent(input, &event) >= 0) { + std.debug.assert(event != null); + if (android.AInputQueue_preDispatchEvent(input, event) != 0) { + continue; + } + + const event_type = @intToEnum(android.AInputEventType, android.AInputEvent_getType(event)); + const handled = switch (event_type) { + .AINPUT_EVENT_TYPE_KEY => try self.processKeyEvent(event.?), + .AINPUT_EVENT_TYPE_MOTION => try self.processMotionEvent(event.?), + else => blk: { + std.log.scoped(.input).debug("Unhandled input event type ({})\n", .{event_type}); + break :blk false; + }, + }; + + // if (app.onInputEvent != NULL) + // handled = app.onInputEvent(app, event); + android.AInputQueue_finishEvent(input, event, if (handled) @as(c_int, 1) else @as(c_int, 0)); + } + } + } + + // Render process + { + // same for the EGL context + self.egl_lock.lock(); + defer self.egl_lock.unlock(); + if (self.egl) |egl| { + try egl.makeCurrent(); + + if (self.egl_init) { + enableDebug(); + app_log.info( + \\GL Vendor: {s} + \\GL Renderer: {s} + \\GL Version: {s} + \\GL Extensions: {s} + \\ + , .{ + std.mem.span(c.glGetString(c.GL_VENDOR)), + std.mem.span(c.glGetString(c.GL_RENDERER)), + std.mem.span(c.glGetString(c.GL_VERSION)), + std.mem.span(c.glGetString(c.GL_EXTENSIONS)), + }); + + touch_program = c.glCreateProgram(); + { + var ps = c.glCreateShader(c.GL_VERTEX_SHADER); + var fs = c.glCreateShader(c.GL_FRAGMENT_SHADER); + + var ps_code = + \\attribute vec2 vPosition; + \\varying vec2 uv; + \\void main() { + \\ uv = vPosition; + \\ gl_Position = vec4(2.0 * uv - 1.0, 0.0, 1.0); + \\} + \\ + ; + var fs_code = + \\varying highp vec2 uv; + \\uniform highp vec2 uPos; + \\uniform highp float uAspect; + \\uniform highp float uIntensity; + \\void main() { + \\ highp vec2 rel = uv - uPos; + \\ rel.x *= uAspect; + \\ gl_FragColor = vec4(vec3(pow(uIntensity * clamp(1.0 - 10.0 * length(rel), 0.0, 1.0), 2.2)), 1.0); + \\} + \\ + ; + + c.glShaderSource(ps, 1, @ptrCast([*c]const [*c]const u8, &ps_code), null); + c.glShaderSource(fs, 1, @ptrCast([*c]const [*c]const u8, &fs_code), null); + + c.glCompileShader(ps); + c.glCompileShader(fs); + + glCheckError(ps); + glCheckError(fs); + + c.glAttachShader(touch_program, ps); + c.glAttachShader(touch_program, fs); + + glShaderInfoLog(ps); + glShaderInfoLog(fs); + + c.glBindAttribLocation(touch_program, 0, "vPosition"); + c.glLinkProgram(touch_program); + + glCheckError(touch_program); + + c.glDetachShader(touch_program, ps); + c.glDetachShader(touch_program, fs); + + glProgramInfoLog(touch_program); + } + + // Get uniform locations + uPos = c.glGetUniformLocation(touch_program, "uPos"); + uAspect = c.glGetUniformLocation(touch_program, "uAspect"); + uIntensity = c.glGetUniformLocation(touch_program, "uIntensity"); + + // Get attrib locations + const vPosition_res = c.glGetAttribLocation(touch_program, "vPosition"); + app_log.info("vPosition: {}", .{vPosition_res}); + vPosition = @intCast(c.GLuint, vPosition_res); + + // Bind the vertices to the buffer + c.glGenBuffers(1, &touch_buffer); + c.glBindBuffer(c.GL_ARRAY_BUFFER, touch_buffer); + c.glBufferData(c.GL_ARRAY_BUFFER, @intCast(isize, vVertices[0..].len * @sizeOf(c.GLfloat)), vVertices[0..], c.GL_STATIC_DRAW); + + shaded_program = c.glCreateProgram(); + { + var ps = c.glCreateShader(c.GL_VERTEX_SHADER); + var fs = c.glCreateShader(c.GL_FRAGMENT_SHADER); + + var ps_code = + \\#version 100 + \\attribute vec3 vPosition; + \\attribute vec3 vNormal; + \\uniform mat4 uTransform; + \\varying vec3 normal; + \\void main() { + \\ normal = mat3(uTransform) * vNormal; + \\ gl_Position = uTransform * vec4(vPosition, 1.0); + \\} + \\ + ; + var fs_code = + \\#version 100 + \\varying highp vec3 normal; + \\void main() { + \\ highp vec3 base_color = vec3(0.968,0.643,0.113); // #F7A41D + \\ highp vec3 ldir = normalize(vec3(0.3, 0.4, 2.0)); + \\ highp float l = 0.3 + 0.8 * clamp(-dot(normal, ldir), 0.0, 1.0); + \\ gl_FragColor = vec4(l * base_color,1); + \\} + \\ + ; + + c.glShaderSource(ps, 1, @ptrCast([*c]const [*c]const u8, &ps_code), null); + c.glShaderSource(fs, 1, @ptrCast([*c]const [*c]const u8, &fs_code), null); + + c.glCompileShader(ps); + c.glCompileShader(fs); + + glShaderInfoLog(ps); + glShaderInfoLog(fs); + + c.glAttachShader(shaded_program, ps); + c.glAttachShader(shaded_program, fs); + + c.glBindAttribLocation(shaded_program, 0, "vPosition"); + c.glBindAttribLocation(shaded_program, 1, "vNormal"); + c.glLinkProgram(shaded_program); + + c.glDetachShader(shaded_program, ps); + c.glDetachShader(shaded_program, fs); + + glProgramInfoLog(shaded_program); + } + + uTransform = c.glGetUniformLocation(shaded_program, "uTransform"); + + // Get attrib locations + const mesh_vPosition_res = c.glGetAttribLocation(shaded_program, "vPosition"); + app_log.info("mesh_vPosition: {}", .{mesh_vPosition_res}); + mesh_vPosition = @intCast(c.GLuint, mesh_vPosition_res); + const mesh_vNormal_res = c.glGetAttribLocation(shaded_program, "vNormal"); + app_log.info("mesh_vNormal: {}", .{mesh_vNormal_res}); + mesh_vNormal = @intCast(c.GLuint, mesh_vNormal_res); + + // Bind the vertices to the buffer + c.glGenBuffers(1, &mesh_buffer); + c.glBindBuffer(c.GL_ARRAY_BUFFER, mesh_buffer); + c.glBufferData(c.GL_ARRAY_BUFFER, @intCast(isize, mesh.len * @sizeOf(MeshVertex)), &mesh, c.GL_STATIC_DRAW); + + self.egl_init = false; + } + + const t = @intToFloat(f32, loop) / 100.0; + + // Clear the screen + c.glClearColor( + 0.5 + 0.5 * @sin(t + 0.0), + 0.5 + 0.5 * @sin(t + 1.0), + 0.5 + 0.5 * @sin(t + 2.0), + 1.0, + ); + c.glClear(c.GL_COLOR_BUFFER_BIT); + + // -- Start touch display code + c.glUseProgram(touch_program); + + c.glBindBuffer(c.GL_ARRAY_BUFFER, touch_buffer); + + c.glEnableVertexAttribArray(vPosition); + c.glVertexAttribPointer(vPosition, 2, c.GL_FLOAT, c.GL_FALSE, 0, @intToPtr(?*anyopaque, 0)); + + // c.glDisableVertexAttribArray(1); + + c.glDisable(c.GL_DEPTH_TEST); + c.glEnable(c.GL_BLEND); + c.glBlendFunc(c.GL_ONE, c.GL_ONE); + c.glBlendEquation(c.GL_FUNC_ADD); + + for (self.touch_points) |*pt| { + if (pt.*) |*point| { + c.glUniform1f(uAspect, self.screen_width / self.screen_height); + c.glUniform2f(uPos, point.x / self.screen_width, 1.0 - point.y / self.screen_height); + c.glUniform1f(uIntensity, point.intensity); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); + + point.intensity -= 0.05; + if (point.intensity <= 0.0) { + if (point.index) |index| { + self.simple_synth.oscillators[@intCast(usize, index)].setWaveOn(false); + } + pt.* = null; + } + } + } + glDrainErrors(); + + // -- Start 3d zig logo code + c.glBindBuffer(c.GL_ARRAY_BUFFER, mesh_buffer); + c.glEnableVertexAttribArray(mesh_vPosition); + c.glVertexAttribPointer(mesh_vPosition, 3, c.GL_FLOAT, c.GL_FALSE, @sizeOf(MeshVertex), @intToPtr(?*anyopaque, @offsetOf(MeshVertex, "pos"))); + + c.glEnableVertexAttribArray(mesh_vNormal); + c.glVertexAttribPointer(mesh_vNormal, 3, c.GL_FLOAT, c.GL_FALSE, @sizeOf(MeshVertex), @intToPtr(?*anyopaque, @offsetOf(MeshVertex, "normal"))); + + c.glUseProgram(shaded_program); + + c.glClearDepthf(1.0); + c.glClear(c.GL_DEPTH_BUFFER_BIT); + + c.glDisable(c.GL_BLEND); + c.glEnable(c.GL_DEPTH_TEST); + + var matrix = [4][4]f32{ + [4]f32{ 1, 0, 0, 0 }, + [4]f32{ 0, 1, 0, 0 }, + [4]f32{ 0, 0, 1, 0 }, + [4]f32{ 0, 0, 0, 1 }, + }; + + matrix[1][1] = self.screen_width / self.screen_height; + + matrix[0][0] = @sin(t); + matrix[2][0] = @cos(t); + matrix[0][2] = @cos(t); + matrix[2][2] = -@sin(t); + + c.glUniformMatrix4fv(uTransform, 1, c.GL_FALSE, @ptrCast([*]const f32, &matrix)); + + c.glDrawArrays(c.GL_TRIANGLES, 0, mesh.len); + + glDrainErrors(); + + try egl.swapBuffers(); + } + } + loop += 1; + + std.time.sleep(10 * std.time.ns_per_ms); + } + app_log.info("mainLoop() finished\n", .{}); + } +}; + +const MeshVertex = extern struct { + pos: Vector4, + normal: Vector4, +}; + +const Vector4 = extern struct { + x: f32, + y: f32, + z: f32, + w: f32 = 1.0, + + fn readFromSlice(slice: []const u8) Vector4 { + return Vector4{ + .x = @bitCast(f32, std.mem.readIntLittle(u32, slice[0..4])), + .y = @bitCast(f32, std.mem.readIntLittle(u32, slice[4..8])), + .z = @bitCast(f32, std.mem.readIntLittle(u32, slice[8..12])), + .w = 1.0, + }; + } +}; + +const mesh = blk: { + const stl_data = @embedFile("logo.stl"); + + const count = std.mem.readIntLittle(u32, stl_data[80..][0..4]); + + var slice: []const u8 = stl_data[84..]; + + var array: [3 * count]MeshVertex = undefined; + var index: usize = 0; + + @setEvalBranchQuota(10_000); + + while (index < count) : (index += 1) { + const normal = Vector4.readFromSlice(slice[0..]); + const v1 = Vector4.readFromSlice(slice[12..]); + const v2 = Vector4.readFromSlice(slice[24..]); + const v3 = Vector4.readFromSlice(slice[36..]); + const attrib_count = std.mem.readIntLittle(u16, slice[48..50]); + + array[3 * index + 0] = MeshVertex{ + .pos = v1, + .normal = normal, + }; + array[3 * index + 1] = MeshVertex{ + .pos = v2, + .normal = normal, + }; + array[3 * index + 2] = MeshVertex{ + .pos = v3, + .normal = normal, + }; + + slice = slice[50 + attrib_count ..]; + } + + break :blk array; +}; + +pub fn glProgramInfoLog(program: c.GLuint) void { + var buffer: [4096]u8 = undefined; + var size: c.GLsizei = undefined; + c.glGetProgramInfoLog(program, 4096, &size, &buffer); + if (size == 0) return; + app_log.info("{s}", .{buffer[0..@intCast(usize, size)]}); +} + +pub fn glShaderInfoLog(shader: c.GLuint) void { + var buffer: [4096]u8 = undefined; + var size: c.GLsizei = undefined; + c.glGetShaderInfoLog(shader, 4096, &size, &buffer); + if (size == 0) return; + app_log.info("{s}", .{buffer[0..@intCast(usize, size)]}); +} + +pub fn glCheckError(res: i64) void { + switch (res) { + c.GL_INVALID_ENUM => app_log.err("GL error code {}: Invalid enum", .{res}), + c.GL_INVALID_VALUE => app_log.err("GL error code {}: Invalid value", .{res}), + c.GL_INVALID_OPERATION => app_log.err("GL error code {}: Invalid operation", .{res}), + // c.GL_STACK_OVERFLOW => app_log.err("GL error code {}: Stack overflow", .{res}), + // c.GL_STACK_UNDERFLOW => app_log.err("GL error code {}: Stack underflow", .{res}), + c.GL_OUT_OF_MEMORY => app_log.err("GL error code {}: Out of memory", .{res}), + // c.GL_TABLE_TOO_LARGE => app_log.err("GL error code {}: Table too large", .{res}), + c.GL_NO_ERROR => {}, + else => {}, + } +} + +pub fn glDrainErrors() void { + var res = c.glGetError(); + while (res != c.GL_NO_ERROR) : (res = c.glGetError()) { + glCheckError(res); + } +} + +pub fn enableDebug() void { + const extensions = std.mem.span(c.glGetString(c.GL_EXTENSIONS)); + if (std.mem.indexOf(u8, extensions, "GL_KHR_debug") != null) { + c.glEnable(c.GL_DEBUG_OUTPUT_KHR); + c.glEnable(c.GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); + + const glDebugMessageControl = @ptrCast(c.PFNGLDEBUGMESSAGECONTROLKHRPROC, c.eglGetProcAddress("glDebugMessageControl")).?; + glDebugMessageControl(c.GL_DONT_CARE, c.GL_DONT_CARE, c.GL_DEBUG_SEVERITY_NOTIFICATION_KHR, 0, null, c.GL_TRUE); + + const glDebugMessageCallback = @ptrCast(c.PFNGLDEBUGMESSAGECALLBACKKHRPROC, c.eglGetProcAddress("glDebugMessageCallback")).?; + glDebugMessageCallback(debugMessageCallback, null); + } else { + app_log.err("Debug is not supported.", .{}); + } +} + +pub fn debugMessageCallback( + source: c.GLenum, + logtype: c.GLenum, + id: c.GLuint, + severity: c.GLenum, + length: c.GLsizei, + message_c: ?[*]const c.GLchar, + user_param: ?*const anyopaque, +) callconv(.C) void { + _ = user_param; + const message = message: { + if (message_c) |message_ptr| { + break :message if (length > 0) message_ptr[0..@intCast(usize, length)] else ""; + } else { + break :message ""; + } + }; + const logtype_str = switch (logtype) { + c.GL_DEBUG_TYPE_ERROR_KHR => "Error", + c.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR => "Deprecated Behavior", + c.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR => "Undefined Behavior", + c.GL_DEBUG_TYPE_PORTABILITY_KHR => "Portability", + c.GL_DEBUG_TYPE_PERFORMANCE_KHR => "Performance", + c.GL_DEBUG_TYPE_OTHER_KHR => "Other", + c.GL_DEBUG_TYPE_MARKER_KHR => "Marker", + else => "Unknown/invalid type", + }; + app_log.err("source = {}, type = {s}, id = {}, severity = {}, message = {s}", .{ source, logtype_str, id, severity, message }); +} + +const Oscillator = struct { + isWaveOn: bool = false, + phase: f64 = 0.0, + phaseIncrement: f64 = 0, + frequency: f64 = 440, + amplitude: f64 = 0.1, + + fn setWaveOn(self: *@This(), isWaveOn: bool) void { + @atomicStore(bool, &self.isWaveOn, isWaveOn, .SeqCst); + } + + fn setSampleRate(self: *@This(), sample_rate: i32) void { + self.phaseIncrement = (std.math.tau * self.frequency) / @intToFloat(f64, sample_rate); + } + + fn renderf32(self: *@This(), audio_data: []f32) void { + if (!@atomicLoad(bool, &self.isWaveOn, .SeqCst)) self.phase = 0; + + for (audio_data) |*frame| { + if (@atomicLoad(bool, &self.isWaveOn, .SeqCst)) { + frame.* += @floatCast(f32, std.math.sin(self.phase) * self.amplitude); + self.phase += self.phaseIncrement; + if (self.phase > std.math.tau) self.phase -= std.math.tau; + } + } + } + + fn renderi16(self: *@This(), audio_data: []i16) void { + if (!@atomicLoad(bool, &self.isWaveOn, .SeqCst)) self.phase = 0; + + for (audio_data) |*frame| { + if (@atomicLoad(bool, &self.isWaveOn, .SeqCst)) { + frame.* +|= @floatToInt(i16, @floatCast(f32, std.math.sin(self.phase) * self.amplitude) * std.math.maxInt(i16)); + self.phase += self.phaseIncrement; + if (self.phase > std.math.tau) self.phase -= std.math.tau; + } + } + } +}; + +const SimpleSynth = struct { + oscillators: [10]Oscillator = [1]Oscillator{.{}} ** 10, + + fn init() SimpleSynth { + var synth = SimpleSynth{}; + for (synth.oscillators) |*osc, index| { + osc.* = Oscillator{ + .frequency = audio.midiToFreq(49 + index * 3), + .amplitude = audio.dBToAmplitude(-@intToFloat(f64, index) - 15), + }; + } + return synth; + } + + fn audioCallback(stream: audio.StreamLayout, user_data: *anyopaque) void { + var synth = @ptrCast(*SimpleSynth, @alignCast(@alignOf(SimpleSynth), user_data)); + std.debug.assert(stream.buffer == .Int16); + + for (synth.oscillators) |*osc| { + osc.setSampleRate(@intCast(i32, stream.sample_rate)); + osc.renderi16(stream.buffer.Int16); + } + } +}; diff --git a/android/src/aaudio.zig b/android/src/aaudio.zig new file mode 100644 index 00000000..56af2c8c --- /dev/null +++ b/android/src/aaudio.zig @@ -0,0 +1,175 @@ +const std = @import("std"); + +const c = @import("c.zig"); + +const OutputStreamConfig = @import("audio.zig").OutputStreamConfig; +const StreamLayout = @import("audio.zig").StreamLayout; + +const audio_log = std.log.scoped(.audio); + +pub const AAudio = struct { + pub const OutputStream = struct { + config: OutputStreamConfig, + stream: ?*c.AAudioStream, + + pub fn start(output_stream: *@This()) !void { + try checkResult(c.AAudioStream_requestStart(output_stream.stream)); + } + + pub fn stop(output_stream: *@This()) void { + checkResult(c.AAudioStream_requestStop(output_stream.stream)) catch |e| { + audio_log.err("Error stopping stream {s}", .{@errorName(e)}); + }; + } + + pub fn deinit(output_stream: *@This()) void { + checkResult(c.AAudioStream_close(output_stream.stream)) catch |e| { + audio_log.err("Error deiniting stream {s}", .{@errorName(e)}); + }; + } + }; + + fn dataCallback( + stream: ?*c.AAudioStream, + user_data: ?*anyopaque, + audio_data: ?*anyopaque, + num_frames: i32, + ) callconv(.C) c.aaudio_data_callback_result_t { + _ = stream; + const output_stream = @ptrCast(*OutputStream, @alignCast(@alignOf(OutputStream), user_data.?)); + // TODO: + // const audio_slice = @ptrCast([*]f32, @alignCast(@alignOf(f32), audio_data.?))[0..@intCast(usize, num_frames)]; + const audio_slice = @ptrCast([*]i16, @alignCast(@alignOf(i16), audio_data.?))[0..@intCast(usize, num_frames)]; + + for (audio_slice) |*frame| { + frame.* = 0; + } + + var stream_layout = StreamLayout{ + .sample_rate = output_stream.config.sample_rate.?, + .channel_count = @intCast(usize, output_stream.config.channel_count), + .buffer = .{ .Int16 = audio_slice }, + }; + + output_stream.config.callback(stream_layout, output_stream.config.user_data); + + return c.AAUDIO_CALLBACK_RESULT_CONTINUE; + } + + fn errorCallback( + stream: ?*c.AAudioStream, + user_data: ?*anyopaque, + err: c.aaudio_result_t, + ) callconv(.C) void { + _ = stream; + audio_log.err("AAudio Stream error! {}", .{err}); + if (err == c.AAUDIO_ERROR_DISCONNECTED) { + const output_stream = @ptrCast(*OutputStream, @alignCast(@alignOf(OutputStream), user_data.?)); + _ = std.Thread.spawn(.{}, OutputStream.deinit, .{output_stream}) catch @panic("Error starting thread for AAudioOutputStream"); + } + } + + pub fn getOutputStream(allocator: std.mem.Allocator, config: OutputStreamConfig) !*OutputStream { + errdefer audio_log.err("Encountered an error with getting output stream", .{}); + // Create a stream builder + var stream_builder: ?*c.AAudioStreamBuilder = null; + checkResult(c.AAudio_createStreamBuilder(&stream_builder)) catch |e| { + audio_log.err("Couldn't create audio stream builder: {s}", .{@errorName(e)}); + return e; + }; + defer checkResult(c.AAudioStreamBuilder_delete(stream_builder)) catch |e| { + // TODO + audio_log.err("Issue with deleting stream builder: {s}", .{@errorName(e)}); + }; + + var output_stream = try allocator.create(OutputStream); + output_stream.* = OutputStream{ + .config = config, + .stream = undefined, + }; + + // Configure the stream + c.AAudioStreamBuilder_setFormat(stream_builder, switch (config.sample_format) { + .Uint8 => return error.Unsupported, + .Int16 => c.AAUDIO_FORMAT_PCM_I16, + .Float32 => c.AAUDIO_FORMAT_PCM_FLOAT, + }); + c.AAudioStreamBuilder_setChannelCount(stream_builder, @intCast(i32, config.channel_count)); + c.AAudioStreamBuilder_setPerformanceMode(stream_builder, c.AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); + c.AAudioStreamBuilder_setDataCallback(stream_builder, dataCallback, output_stream); + c.AAudioStreamBuilder_setErrorCallback(stream_builder, errorCallback, output_stream); + + if (config.sample_rate) |rate| c.AAudioStreamBuilder_setSampleRate(stream_builder, @intCast(i32, rate)); + if (config.buffer_size) |size| c.AAudioStreamBuilder_setFramesPerDataCallback(stream_builder, @intCast(i32, size)); + + // Open the stream + checkResult(c.AAudioStreamBuilder_openStream(stream_builder, &output_stream.stream)) catch |e| { + audio_log.err("Issue with opening stream: {s}", .{@errorName(e)}); + return e; + }; + + // Save the details of the stream + output_stream.config.sample_rate = @intCast(u32, c.AAudioStream_getSampleRate(output_stream.stream)); + output_stream.config.buffer_size = @intCast(usize, c.AAudioStream_getFramesPerBurst(output_stream.stream)); + + var res = c.AAudioStream_setBufferSizeInFrames(output_stream.stream, @intCast(i32, output_stream.config.buffer_count * output_stream.config.buffer_size.?)); + if (res < 0) { + checkResult(res) catch |e| { + audio_log.err("Issue with setting buffer size in frames stream: {s}", .{@errorName(e)}); + return e; + }; + } else { + // TODO: store buffer size somewhere + // output_stream.config. + } + + audio_log.info("Got AAudio OutputStream", .{}); + + return output_stream; + } + + pub const AAudioError = error{ + Base, + Disconnected, + IllegalArgument, + Internal, + InvalidState, + InvalidHandle, + Unimplemented, + Unavailable, + NoFreeHandles, + NoMemory, + Null, + Timeout, + WouldBlock, + InvalidFormat, + OutOfRange, + NoService, + InvalidRate, + Unknown, + }; + + pub fn checkResult(result: c.aaudio_result_t) AAudioError!void { + return switch (result) { + c.AAUDIO_OK => {}, + c.AAUDIO_ERROR_BASE => error.Base, + c.AAUDIO_ERROR_DISCONNECTED => error.Disconnected, + c.AAUDIO_ERROR_ILLEGAL_ARGUMENT => error.IllegalArgument, + c.AAUDIO_ERROR_INTERNAL => error.Internal, + c.AAUDIO_ERROR_INVALID_STATE => error.InvalidState, + c.AAUDIO_ERROR_INVALID_HANDLE => error.InvalidHandle, + c.AAUDIO_ERROR_UNIMPLEMENTED => error.Unimplemented, + c.AAUDIO_ERROR_UNAVAILABLE => error.Unavailable, + c.AAUDIO_ERROR_NO_FREE_HANDLES => error.NoFreeHandles, + c.AAUDIO_ERROR_NO_MEMORY => error.NoMemory, + c.AAUDIO_ERROR_NULL => error.Null, + c.AAUDIO_ERROR_TIMEOUT => error.Timeout, + c.AAUDIO_ERROR_WOULD_BLOCK => error.WouldBlock, + c.AAUDIO_ERROR_INVALID_FORMAT => error.InvalidFormat, + c.AAUDIO_ERROR_OUT_OF_RANGE => error.OutOfRange, + c.AAUDIO_ERROR_NO_SERVICE => error.NoService, + c.AAUDIO_ERROR_INVALID_RATE => error.InvalidRate, + else => error.Unknown, + }; + } +}; diff --git a/android/src/android-bind.zig b/android/src/android-bind.zig new file mode 100644 index 00000000..3e051a67 --- /dev/null +++ b/android/src/android-bind.zig @@ -0,0 +1,2623 @@ +const __builtin_va_list = extern struct { + padding: u32, +}; + +pub const va_list = __builtin_va_list; +pub const __gnuc_va_list = __builtin_va_list; +pub const ANDROID_LOG_UNKNOWN = @enumToInt(enum_android_LogPriority.ANDROID_LOG_UNKNOWN); +pub const ANDROID_LOG_DEFAULT = @enumToInt(enum_android_LogPriority.ANDROID_LOG_DEFAULT); +pub const ANDROID_LOG_VERBOSE = @enumToInt(enum_android_LogPriority.ANDROID_LOG_VERBOSE); +pub const ANDROID_LOG_DEBUG = @enumToInt(enum_android_LogPriority.ANDROID_LOG_DEBUG); +pub const ANDROID_LOG_INFO = @enumToInt(enum_android_LogPriority.ANDROID_LOG_INFO); +pub const ANDROID_LOG_WARN = @enumToInt(enum_android_LogPriority.ANDROID_LOG_WARN); +pub const ANDROID_LOG_ERROR = @enumToInt(enum_android_LogPriority.ANDROID_LOG_ERROR); +pub const ANDROID_LOG_FATAL = @enumToInt(enum_android_LogPriority.ANDROID_LOG_FATAL); +pub const ANDROID_LOG_SILENT = @enumToInt(enum_android_LogPriority.ANDROID_LOG_SILENT); +pub const enum_android_LogPriority = enum(c_int) { + ANDROID_LOG_UNKNOWN = 0, + ANDROID_LOG_DEFAULT = 1, + ANDROID_LOG_VERBOSE = 2, + ANDROID_LOG_DEBUG = 3, + ANDROID_LOG_INFO = 4, + ANDROID_LOG_WARN = 5, + ANDROID_LOG_ERROR = 6, + ANDROID_LOG_FATAL = 7, + ANDROID_LOG_SILENT = 8, + _, +}; +pub const android_LogPriority = enum_android_LogPriority; +pub extern fn __android_log_write(prio: c_int, tag: [*c]const u8, text: [*c]const u8) c_int; +pub extern fn __android_log_print(prio: c_int, tag: [*c]const u8, fmt: [*c]const u8, ...) c_int; +pub extern fn __android_log_vprint(prio: c_int, tag: [*c]const u8, fmt: [*c]const u8, ap: va_list) c_int; +pub extern fn __android_log_assert(cond: [*c]const u8, tag: [*c]const u8, fmt: [*c]const u8, ...) noreturn; +pub const LOG_ID_MIN = @enumToInt(enum_log_id.LOG_ID_MIN); +pub const LOG_ID_MAIN = @enumToInt(enum_log_id.LOG_ID_MAIN); +pub const LOG_ID_RADIO = @enumToInt(enum_log_id.LOG_ID_RADIO); +pub const LOG_ID_EVENTS = @enumToInt(enum_log_id.LOG_ID_EVENTS); +pub const LOG_ID_SYSTEM = @enumToInt(enum_log_id.LOG_ID_SYSTEM); +pub const LOG_ID_CRASH = @enumToInt(enum_log_id.LOG_ID_CRASH); +pub const LOG_ID_STATS = @enumToInt(enum_log_id.LOG_ID_STATS); +pub const LOG_ID_SECURITY = @enumToInt(enum_log_id.LOG_ID_SECURITY); +pub const LOG_ID_KERNEL = @enumToInt(enum_log_id.LOG_ID_KERNEL); +pub const LOG_ID_MAX = @enumToInt(enum_log_id.LOG_ID_MAX); +pub const enum_log_id = enum(c_int) { + LOG_ID_MIN = 0, + LOG_ID_MAIN = 0, + LOG_ID_RADIO = 1, + LOG_ID_EVENTS = 2, + LOG_ID_SYSTEM = 3, + LOG_ID_CRASH = 4, + LOG_ID_STATS = 5, + LOG_ID_SECURITY = 6, + LOG_ID_KERNEL = 7, + LOG_ID_MAX = 8, + _, +}; +pub const log_id_t = enum_log_id; +pub extern fn __android_log_buf_write(bufID: c_int, prio: c_int, tag: [*c]const u8, text: [*c]const u8) c_int; +pub extern fn __android_log_buf_print(bufID: c_int, prio: c_int, tag: [*c]const u8, fmt: [*c]const u8, ...) c_int; +pub extern fn android_get_application_target_sdk_version(...) c_int; +pub extern fn android_get_device_api_level(...) c_int; +pub const ptrdiff_t = c_long; +pub const wchar_t = c_uint; +const struct_unnamed_1 = extern struct { + __clang_max_align_nonce1: c_longlong align(8), + __clang_max_align_nonce2: c_longdouble align(16), +}; +pub const max_align_t = struct_unnamed_1; +pub const __int8_t = i8; +pub const __uint8_t = u8; +pub const __int16_t = c_short; +pub const __uint16_t = c_ushort; +pub const __int32_t = c_int; +pub const __uint32_t = c_uint; +pub const __int64_t = c_long; +pub const __uint64_t = c_ulong; +pub const __intptr_t = c_long; +pub const __uintptr_t = c_ulong; +pub const int_least8_t = i8; +pub const uint_least8_t = u8; +pub const int_least16_t = i16; +pub const uint_least16_t = u16; +pub const int_least32_t = i32; +pub const uint_least32_t = u32; +pub const int_least64_t = i64; +pub const uint_least64_t = u64; +pub const int_fast8_t = i8; +pub const uint_fast8_t = u8; +pub const int_fast64_t = i64; +pub const uint_fast64_t = u64; +pub const int_fast16_t = i64; +pub const uint_fast16_t = u64; +pub const int_fast32_t = i64; +pub const uint_fast32_t = u64; +pub const uintmax_t = u64; +pub const intmax_t = i64; +pub const __s8 = i8; +pub const __u8 = u8; +pub const __s16 = c_short; +pub const __u16 = c_ushort; +pub const __s32 = c_int; +pub const __u32 = c_uint; +pub const __s64 = c_longlong; +pub const __u64 = c_ulonglong; +const struct_unnamed_2 = extern struct { + fds_bits: [16]c_ulong, +}; +pub const __kernel_fd_set = struct_unnamed_2; +pub const __kernel_sighandler_t = ?*const fn (c_int) callconv(.C) void; +pub const __kernel_key_t = c_int; +pub const __kernel_mqd_t = c_int; +pub const __kernel_old_uid_t = c_ushort; +pub const __kernel_old_gid_t = c_ushort; +pub const __kernel_long_t = c_long; +pub const __kernel_ulong_t = c_ulong; +pub const __kernel_ino_t = __kernel_ulong_t; +pub const __kernel_mode_t = c_uint; +pub const __kernel_pid_t = c_int; +pub const __kernel_ipc_pid_t = c_int; +pub const __kernel_uid_t = c_uint; +pub const __kernel_gid_t = c_uint; +pub const __kernel_suseconds_t = __kernel_long_t; +pub const __kernel_daddr_t = c_int; +pub const __kernel_uid32_t = c_uint; +pub const __kernel_gid32_t = c_uint; +pub const __kernel_old_dev_t = c_uint; +pub const __kernel_size_t = __kernel_ulong_t; +pub const __kernel_ssize_t = __kernel_long_t; +pub const __kernel_ptrdiff_t = __kernel_long_t; +const struct_unnamed_3 = extern struct { + val: [2]c_int, +}; +pub const __kernel_fsid_t = struct_unnamed_3; +pub const __kernel_off_t = __kernel_long_t; +pub const __kernel_loff_t = c_longlong; +pub const __kernel_time_t = __kernel_long_t; +pub const __kernel_time64_t = c_longlong; +pub const __kernel_clock_t = __kernel_long_t; +pub const __kernel_timer_t = c_int; +pub const __kernel_clockid_t = c_int; +pub const __kernel_caddr_t = [*c]u8; +pub const __kernel_uid16_t = c_ushort; +pub const __kernel_gid16_t = c_ushort; +pub const __le16 = __u16; +pub const __be16 = __u16; +pub const __le32 = __u32; +pub const __be32 = __u32; +pub const __le64 = __u64; +pub const __be64 = __u64; +pub const __sum16 = __u16; +pub const __wsum = __u32; +pub const __poll_t = c_uint; +const struct_unnamed_4 = extern struct { + flags: u32, + stack_base: ?*anyopaque, + stack_size: usize, + guard_size: usize, + sched_policy: i32, + sched_priority: i32, + __reserved: [16]u8, +}; +pub const pthread_attr_t = struct_unnamed_4; +const struct_unnamed_5 = extern struct { + __private: [4]i64, +}; +pub const pthread_barrier_t = struct_unnamed_5; +pub const pthread_barrierattr_t = c_int; +const struct_unnamed_6 = extern struct { + __private: [12]i32, +}; +pub const pthread_cond_t = struct_unnamed_6; +pub const pthread_condattr_t = c_long; +pub const pthread_key_t = c_int; +const struct_unnamed_7 = extern struct { + __private: [10]i32, +}; +pub const pthread_mutex_t = struct_unnamed_7; +pub const pthread_mutexattr_t = c_long; +pub const pthread_once_t = c_int; +const struct_unnamed_8 = extern struct { + __private: [14]i32, +}; +pub const pthread_rwlock_t = struct_unnamed_8; +pub const pthread_rwlockattr_t = c_long; +const struct_unnamed_9 = extern struct { + __private: i64, +}; +pub const pthread_spinlock_t = struct_unnamed_9; +pub const pthread_t = c_long; +pub const __gid_t = __kernel_gid32_t; +pub const gid_t = __gid_t; +pub const __uid_t = __kernel_uid32_t; +pub const uid_t = __uid_t; +pub const __pid_t = __kernel_pid_t; +pub const pid_t = __pid_t; +pub const __id_t = u32; +pub const id_t = __id_t; +pub const blkcnt_t = c_ulong; +pub const blksize_t = c_ulong; +pub const caddr_t = __kernel_caddr_t; +pub const clock_t = __kernel_clock_t; +pub const __clockid_t = __kernel_clockid_t; +pub const clockid_t = __clockid_t; +pub const daddr_t = __kernel_daddr_t; +pub const fsblkcnt_t = c_ulong; +pub const fsfilcnt_t = c_ulong; +pub const __mode_t = __kernel_mode_t; +pub const mode_t = __mode_t; +pub const __key_t = __kernel_key_t; +pub const key_t = __key_t; +pub const __ino_t = __kernel_ino_t; +pub const ino_t = __ino_t; +pub const ino64_t = u64; +pub const __nlink_t = u32; +pub const nlink_t = __nlink_t; +pub const __timer_t = ?*anyopaque; +pub const timer_t = __timer_t; +pub const __suseconds_t = __kernel_suseconds_t; +pub const suseconds_t = __suseconds_t; +pub const __useconds_t = u32; +pub const useconds_t = __useconds_t; +pub const dev_t = u64; +pub const __time_t = __kernel_time_t; +pub const time_t = __time_t; +pub const off_t = i64; +pub const loff_t = off_t; +pub const off64_t = loff_t; +pub const __socklen_t = u32; +pub const socklen_t = __socklen_t; +pub const __va_list = __builtin_va_list; +pub const uint_t = c_uint; +pub const uint = c_uint; +pub const u_char = u8; +pub const u_short = c_ushort; +pub const u_int = c_uint; +pub const u_long = c_ulong; +pub const u_int32_t = u32; +pub const u_int16_t = u16; +pub const u_int8_t = u8; +pub const u_int64_t = u64; +pub const AAssetManager = opaque {}; +pub const AAssetDir = opaque {}; +pub const AAsset = opaque {}; +pub const AASSET_MODE_UNKNOWN = @enumToInt(enum_unnamed_10.AASSET_MODE_UNKNOWN); +pub const AASSET_MODE_RANDOM = @enumToInt(enum_unnamed_10.AASSET_MODE_RANDOM); +pub const AASSET_MODE_STREAMING = @enumToInt(enum_unnamed_10.AASSET_MODE_STREAMING); +pub const AASSET_MODE_BUFFER = @enumToInt(enum_unnamed_10.AASSET_MODE_BUFFER); +const enum_unnamed_10 = enum(c_int) { + AASSET_MODE_UNKNOWN = 0, + AASSET_MODE_RANDOM = 1, + AASSET_MODE_STREAMING = 2, + AASSET_MODE_BUFFER = 3, + _, +}; +pub extern fn AAssetManager_openDir(mgr: ?*AAssetManager, dirName: [*c]const u8) ?*AAssetDir; +pub extern fn AAssetManager_open(mgr: ?*AAssetManager, filename: [*c]const u8, mode: c_int) ?*AAsset; +pub extern fn AAssetDir_getNextFileName(assetDir: ?*AAssetDir) [*c]const u8; +pub extern fn AAssetDir_rewind(assetDir: ?*AAssetDir) void; +pub extern fn AAssetDir_close(assetDir: ?*AAssetDir) void; +pub extern fn AAsset_read(asset: ?*AAsset, buf: ?*anyopaque, count: usize) c_int; +pub extern fn AAsset_seek(asset: ?*AAsset, offset: off_t, whence: c_int) off_t; +pub extern fn AAsset_seek64(asset: ?*AAsset, offset: off64_t, whence: c_int) off64_t; +pub extern fn AAsset_close(asset: ?*AAsset) void; +pub extern fn AAsset_getBuffer(asset: ?*AAsset) ?*const anyopaque; +pub extern fn AAsset_getLength(asset: ?*AAsset) off_t; +pub extern fn AAsset_getLength64(asset: ?*AAsset) off64_t; +pub extern fn AAsset_getRemainingLength(asset: ?*AAsset) off_t; +pub extern fn AAsset_getRemainingLength64(asset: ?*AAsset) off64_t; +pub extern fn AAsset_openFileDescriptor(asset: ?*AAsset, outStart: [*c]off_t, outLength: [*c]off_t) c_int; +pub extern fn AAsset_openFileDescriptor64(asset: ?*AAsset, outStart: [*c]off64_t, outLength: [*c]off64_t) c_int; +pub extern fn AAsset_isAllocated(asset: ?*AAsset) c_int; + +pub const AConfiguration = opaque {}; +pub const ACONFIGURATION_ORIENTATION_ANY = 0; +pub const ACONFIGURATION_ORIENTATION_PORT = 1; +pub const ACONFIGURATION_ORIENTATION_LAND = 2; +pub const ACONFIGURATION_ORIENTATION_SQUARE = 3; +pub const ACONFIGURATION_TOUCHSCREEN_ANY = 0; +pub const ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 1; +pub const ACONFIGURATION_TOUCHSCREEN_STYLUS = 2; +pub const ACONFIGURATION_TOUCHSCREEN_FINGER = 3; +pub const ACONFIGURATION_DENSITY_DEFAULT = 0; +pub const ACONFIGURATION_DENSITY_LOW = 120; +pub const ACONFIGURATION_DENSITY_MEDIUM = 160; +pub const ACONFIGURATION_DENSITY_TV = 213; +pub const ACONFIGURATION_DENSITY_HIGH = 240; +pub const ACONFIGURATION_DENSITY_XHIGH = 320; +pub const ACONFIGURATION_DENSITY_XXHIGH = 480; +pub const ACONFIGURATION_DENSITY_XXXHIGH = 640; +pub const ACONFIGURATION_DENSITY_ANY = 65534; +pub const ACONFIGURATION_DENSITY_NONE = 65535; +pub const ACONFIGURATION_KEYBOARD_ANY = 0; +pub const ACONFIGURATION_KEYBOARD_NOKEYS = 1; +pub const ACONFIGURATION_KEYBOARD_QWERTY = 2; +pub const ACONFIGURATION_KEYBOARD_12KEY = 3; +pub const ACONFIGURATION_NAVIGATION_ANY = 0; +pub const ACONFIGURATION_NAVIGATION_NONAV = 1; +pub const ACONFIGURATION_NAVIGATION_DPAD = 2; +pub const ACONFIGURATION_NAVIGATION_TRACKBALL = 3; +pub const ACONFIGURATION_NAVIGATION_WHEEL = 4; +pub const ACONFIGURATION_KEYSHIDDEN_ANY = 0; +pub const ACONFIGURATION_KEYSHIDDEN_NO = 1; +pub const ACONFIGURATION_KEYSHIDDEN_YES = 2; +pub const ACONFIGURATION_KEYSHIDDEN_SOFT = 3; +pub const ACONFIGURATION_NAVHIDDEN_ANY = 0; +pub const ACONFIGURATION_NAVHIDDEN_NO = 1; +pub const ACONFIGURATION_NAVHIDDEN_YES = 2; +pub const ACONFIGURATION_SCREENSIZE_ANY = 0; +pub const ACONFIGURATION_SCREENSIZE_SMALL = 1; +pub const ACONFIGURATION_SCREENSIZE_NORMAL = 2; +pub const ACONFIGURATION_SCREENSIZE_LARGE = 3; +pub const ACONFIGURATION_SCREENSIZE_XLARGE = 4; +pub const ACONFIGURATION_SCREENLONG_ANY = 0; +pub const ACONFIGURATION_SCREENLONG_NO = 1; +pub const ACONFIGURATION_SCREENLONG_YES = 2; +pub const ACONFIGURATION_SCREENROUND_ANY = 0; +pub const ACONFIGURATION_SCREENROUND_NO = 1; +pub const ACONFIGURATION_SCREENROUND_YES = 2; +pub const ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0; +pub const ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 1; +pub const ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 2; +pub const ACONFIGURATION_HDR_ANY = 0; +pub const ACONFIGURATION_HDR_NO = 1; +pub const ACONFIGURATION_HDR_YES = 2; +pub const ACONFIGURATION_UI_MODE_TYPE_ANY = 0; +pub const ACONFIGURATION_UI_MODE_TYPE_NORMAL = 1; +pub const ACONFIGURATION_UI_MODE_TYPE_DESK = 2; +pub const ACONFIGURATION_UI_MODE_TYPE_CAR = 3; +pub const ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 4; +pub const ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 5; +pub const ACONFIGURATION_UI_MODE_TYPE_WATCH = 6; +pub const ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 7; +pub const ACONFIGURATION_UI_MODE_NIGHT_ANY = 0; +pub const ACONFIGURATION_UI_MODE_NIGHT_NO = 1; +pub const ACONFIGURATION_UI_MODE_NIGHT_YES = 2; +pub const ACONFIGURATION_SCREEN_WIDTH_DP_ANY = 0; +pub const ACONFIGURATION_SCREEN_HEIGHT_DP_ANY = 0; +pub const ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY = 0; +pub const ACONFIGURATION_LAYOUTDIR_ANY = 0; +pub const ACONFIGURATION_LAYOUTDIR_LTR = 1; +pub const ACONFIGURATION_LAYOUTDIR_RTL = 2; +pub const ACONFIGURATION_MCC = 1; +pub const ACONFIGURATION_MNC = 2; +pub const ACONFIGURATION_LOCALE = 4; +pub const ACONFIGURATION_TOUCHSCREEN = 8; +pub const ACONFIGURATION_KEYBOARD = 16; +pub const ACONFIGURATION_KEYBOARD_HIDDEN = 32; +pub const ACONFIGURATION_NAVIGATION = 64; +pub const ACONFIGURATION_ORIENTATION = 128; +pub const ACONFIGURATION_DENSITY = 256; +pub const ACONFIGURATION_SCREEN_SIZE = 512; +pub const ACONFIGURATION_VERSION = 1024; +pub const ACONFIGURATION_SCREEN_LAYOUT = 2048; +pub const ACONFIGURATION_UI_MODE = 4096; +pub const ACONFIGURATION_SMALLEST_SCREEN_SIZE = 8192; +pub const ACONFIGURATION_LAYOUTDIR = 16384; +pub const ACONFIGURATION_SCREEN_ROUND = 32768; +pub const ACONFIGURATION_COLOR_MODE = 65536; +pub const ACONFIGURATION_MNC_ZERO = 65535; + +pub extern fn AConfiguration_new(...) ?*AConfiguration; +pub extern fn AConfiguration_delete(config: ?*AConfiguration) void; +pub extern fn AConfiguration_fromAssetManager(out: ?*AConfiguration, am: ?*AAssetManager) void; +pub extern fn AConfiguration_copy(dest: ?*AConfiguration, src: ?*AConfiguration) void; +pub extern fn AConfiguration_getMcc(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setMcc(config: ?*AConfiguration, mcc: i32) void; +pub extern fn AConfiguration_getMnc(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setMnc(config: ?*AConfiguration, mnc: i32) void; +pub extern fn AConfiguration_getLanguage(config: ?*AConfiguration, outLanguage: [*c]u8) void; +pub extern fn AConfiguration_setLanguage(config: ?*AConfiguration, language: [*c]const u8) void; +pub extern fn AConfiguration_getCountry(config: ?*AConfiguration, outCountry: [*c]u8) void; +pub extern fn AConfiguration_setCountry(config: ?*AConfiguration, country: [*c]const u8) void; +pub extern fn AConfiguration_getOrientation(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setOrientation(config: ?*AConfiguration, orientation: i32) void; +pub extern fn AConfiguration_getTouchscreen(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setTouchscreen(config: ?*AConfiguration, touchscreen: i32) void; +pub extern fn AConfiguration_getDensity(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setDensity(config: ?*AConfiguration, density: i32) void; +pub extern fn AConfiguration_getKeyboard(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setKeyboard(config: ?*AConfiguration, keyboard: i32) void; +pub extern fn AConfiguration_getNavigation(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setNavigation(config: ?*AConfiguration, navigation: i32) void; +pub extern fn AConfiguration_getKeysHidden(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setKeysHidden(config: ?*AConfiguration, keysHidden: i32) void; +pub extern fn AConfiguration_getNavHidden(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setNavHidden(config: ?*AConfiguration, navHidden: i32) void; +pub extern fn AConfiguration_getSdkVersion(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setSdkVersion(config: ?*AConfiguration, sdkVersion: i32) void; +pub extern fn AConfiguration_getScreenSize(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setScreenSize(config: ?*AConfiguration, screenSize: i32) void; +pub extern fn AConfiguration_getScreenLong(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setScreenLong(config: ?*AConfiguration, screenLong: i32) void; +pub extern fn AConfiguration_getScreenRound(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setScreenRound(config: ?*AConfiguration, screenRound: i32) void; +pub extern fn AConfiguration_getUiModeType(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setUiModeType(config: ?*AConfiguration, uiModeType: i32) void; +pub extern fn AConfiguration_getUiModeNight(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setUiModeNight(config: ?*AConfiguration, uiModeNight: i32) void; +pub extern fn AConfiguration_getScreenWidthDp(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setScreenWidthDp(config: ?*AConfiguration, value: i32) void; +pub extern fn AConfiguration_getScreenHeightDp(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setScreenHeightDp(config: ?*AConfiguration, value: i32) void; +pub extern fn AConfiguration_getSmallestScreenWidthDp(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setSmallestScreenWidthDp(config: ?*AConfiguration, value: i32) void; +pub extern fn AConfiguration_getLayoutDirection(config: ?*AConfiguration) i32; +pub extern fn AConfiguration_setLayoutDirection(config: ?*AConfiguration, value: i32) void; +pub extern fn AConfiguration_diff(config1: ?*AConfiguration, config2: ?*AConfiguration) i32; +pub extern fn AConfiguration_match(base: ?*AConfiguration, requested: ?*AConfiguration) i32; +pub extern fn AConfiguration_isBetterThan(base: ?*AConfiguration, @"test": ?*AConfiguration, requested: ?*AConfiguration) i32; +pub const struct_ALooper = opaque {}; +pub const ALooper = struct_ALooper; +pub extern fn ALooper_forThread(...) ?*ALooper; +pub const ALOOPER_PREPARE_ALLOW_NON_CALLBACKS = @enumToInt(enum_unnamed_12.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); +const enum_unnamed_12 = enum(c_int) { + ALOOPER_PREPARE_ALLOW_NON_CALLBACKS = 1, + _, +}; +pub extern fn ALooper_prepare(opts: c_int) ?*ALooper; +pub const ALOOPER_POLL_WAKE = @enumToInt(enum_unnamed_13.ALOOPER_POLL_WAKE); +pub const ALOOPER_POLL_CALLBACK = @enumToInt(enum_unnamed_13.ALOOPER_POLL_CALLBACK); +pub const ALOOPER_POLL_TIMEOUT = @enumToInt(enum_unnamed_13.ALOOPER_POLL_TIMEOUT); +pub const ALOOPER_POLL_ERROR = @enumToInt(enum_unnamed_13.ALOOPER_POLL_ERROR); +const enum_unnamed_13 = enum(c_int) { + ALOOPER_POLL_WAKE = -1, + ALOOPER_POLL_CALLBACK = -2, + ALOOPER_POLL_TIMEOUT = -3, + ALOOPER_POLL_ERROR = -4, + _, +}; +pub extern fn ALooper_acquire(looper: ?*ALooper) void; +pub extern fn ALooper_release(looper: ?*ALooper) void; +pub const ALOOPER_EVENT_INPUT = @enumToInt(enum_unnamed_14.ALOOPER_EVENT_INPUT); +pub const ALOOPER_EVENT_OUTPUT = @enumToInt(enum_unnamed_14.ALOOPER_EVENT_OUTPUT); +pub const ALOOPER_EVENT_ERROR = @enumToInt(enum_unnamed_14.ALOOPER_EVENT_ERROR); +pub const ALOOPER_EVENT_HANGUP = @enumToInt(enum_unnamed_14.ALOOPER_EVENT_HANGUP); +pub const ALOOPER_EVENT_INVALID = @enumToInt(enum_unnamed_14.ALOOPER_EVENT_INVALID); +const enum_unnamed_14 = enum(c_int) { + ALOOPER_EVENT_INPUT = 1, + ALOOPER_EVENT_OUTPUT = 2, + ALOOPER_EVENT_ERROR = 4, + ALOOPER_EVENT_HANGUP = 8, + ALOOPER_EVENT_INVALID = 16, + _, +}; +pub const ALooper_callbackFunc = ?*const fn (c_int, c_int, ?*anyopaque) callconv(.C) c_int; +pub extern fn ALooper_pollOnce(timeoutMillis: c_int, outFd: [*c]c_int, outEvents: [*c]c_int, outData: [*c]?*anyopaque) c_int; +pub extern fn ALooper_pollAll(timeoutMillis: c_int, outFd: [*c]c_int, outEvents: [*c]c_int, outData: [*c]?*anyopaque) c_int; +pub extern fn ALooper_wake(looper: ?*ALooper) void; +pub extern fn ALooper_addFd(looper: ?*ALooper, fd: c_int, ident: c_int, events: c_int, callback: ALooper_callbackFunc, data: ?*anyopaque) c_int; +pub extern fn ALooper_removeFd(looper: ?*ALooper, fd: c_int) c_int; +pub const jboolean = u8; +pub const jbyte = i8; +pub const jchar = u16; +pub const jshort = i16; +pub const jint = i32; +pub const jlong = i64; +pub const jfloat = f32; +pub const jdouble = f64; +pub const jsize = jint; +pub const jobject = ?*anyopaque; +pub const jclass = jobject; +pub const jstring = jobject; +pub const jarray = jobject; +pub const jobjectArray = jarray; +pub const jbooleanArray = jarray; +pub const jbyteArray = jarray; +pub const jcharArray = jarray; +pub const jshortArray = jarray; +pub const jintArray = jarray; +pub const jlongArray = jarray; +pub const jfloatArray = jarray; +pub const jdoubleArray = jarray; +pub const jthrowable = jobject; +pub const jweak = jobject; +pub const struct__jfieldID = opaque {}; +pub const jfieldID = ?*struct__jfieldID; +pub const struct__jmethodID = opaque {}; +pub const jmethodID = ?*struct__jmethodID; +pub const struct_JNIInvokeInterface = extern struct { + reserved0: ?*anyopaque, + reserved1: ?*anyopaque, + reserved2: ?*anyopaque, + DestroyJavaVM: *const fn (*JavaVM) callconv(.C) jint, + AttachCurrentThread: *const fn (*JavaVM, **JNIEnv, ?*anyopaque) callconv(.C) jint, + DetachCurrentThread: *const fn (*JavaVM) callconv(.C) jint, + GetEnv: *const fn (*JavaVM, *?*anyopaque, jint) callconv(.C) jint, + AttachCurrentThreadAsDaemon: *const fn (*JavaVM, **JNIEnv, ?*anyopaque) callconv(.C) jint, +}; +pub const union_jvalue = extern union { + z: jboolean, + b: jbyte, + c: jchar, + s: jshort, + i: jint, + j: jlong, + f: jfloat, + d: jdouble, + l: jobject, +}; +pub const jvalue = union_jvalue; +pub const JNIInvalidRefType = @enumToInt(enum_jobjectRefType.JNIInvalidRefType); +pub const JNILocalRefType = @enumToInt(enum_jobjectRefType.JNILocalRefType); +pub const JNIGlobalRefType = @enumToInt(enum_jobjectRefType.JNIGlobalRefType); +pub const JNIWeakGlobalRefType = @enumToInt(enum_jobjectRefType.JNIWeakGlobalRefType); +pub const enum_jobjectRefType = enum(c_int) { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3, + _, +}; +pub const jobjectRefType = enum_jobjectRefType; +const struct_unnamed_15 = extern struct { + name: [*c]const u8, + signature: [*c]const u8, + fnPtr: ?*anyopaque, +}; +pub const JNINativeMethod = struct_unnamed_15; +pub const JNINativeInterface = extern struct { + reserved0: ?*anyopaque, + reserved1: ?*anyopaque, + reserved2: ?*anyopaque, + reserved3: ?*anyopaque, + GetVersion: *const fn (*JNIEnv) callconv(.C) jint, + DefineClass: *const fn (*JNIEnv, [*:0]const u8, jobject, [*c]const jbyte, jsize) callconv(.C) jclass, + FindClass: *const fn (*JNIEnv, [*:0]const u8) callconv(.C) jclass, + FromReflectedMethod: *const fn (*JNIEnv, jobject) callconv(.C) jmethodID, + FromReflectedField: *const fn (*JNIEnv, jobject) callconv(.C) jfieldID, + ToReflectedMethod: *const fn (*JNIEnv, jclass, jmethodID, jboolean) callconv(.C) jobject, + GetSuperclass: *const fn (*JNIEnv, jclass) callconv(.C) jclass, + IsAssignableFrom: *const fn (*JNIEnv, jclass, jclass) callconv(.C) jboolean, + ToReflectedField: *const fn (*JNIEnv, jclass, jfieldID, jboolean) callconv(.C) jobject, + Throw: *const fn (*JNIEnv, jthrowable) callconv(.C) jint, + ThrowNew: *const fn (*JNIEnv, jclass, [*:0]const u8) callconv(.C) jint, + ExceptionOccurred: *const fn (*JNIEnv) callconv(.C) jthrowable, + ExceptionDescribe: *const fn (*JNIEnv) callconv(.C) void, + ExceptionClear: *const fn (*JNIEnv) callconv(.C) void, + FatalError: *const fn (*JNIEnv, [*:0]const u8) callconv(.C) void, + PushLocalFrame: *const fn (*JNIEnv, jint) callconv(.C) jint, + PopLocalFrame: *const fn (*JNIEnv, jobject) callconv(.C) jobject, + NewGlobalRef: *const fn (*JNIEnv, jobject) callconv(.C) jobject, + DeleteGlobalRef: *const fn (*JNIEnv, jobject) callconv(.C) void, + DeleteLocalRef: *const fn (*JNIEnv, jobject) callconv(.C) void, + IsSameObject: *const fn (*JNIEnv, jobject, jobject) callconv(.C) jboolean, + NewLocalRef: *const fn (*JNIEnv, jobject) callconv(.C) jobject, + EnsureLocalCapacity: *const fn (*JNIEnv, jint) callconv(.C) jint, + AllocObject: *const fn (*JNIEnv, jclass) callconv(.C) jobject, + NewObject: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jobject, + NewObjectV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jobject, + NewObjectA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jobject, + GetObjectClass: *const fn (*JNIEnv, jobject) callconv(.C) jclass, + IsInstanceOf: *const fn (*JNIEnv, jobject, jclass) callconv(.C) jboolean, + GetMethodID: *const fn (*JNIEnv, jclass, [*:0]const u8, [*:0]const u8) callconv(.C) jmethodID, + CallObjectMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jobject, + CallObjectMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jobject, + CallObjectMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jobject, + CallBooleanMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jboolean, + CallBooleanMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jboolean, + CallBooleanMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jboolean, + CallByteMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jbyte, + CallByteMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jbyte, + CallByteMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jbyte, + CallCharMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jchar, + CallCharMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jchar, + CallCharMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jchar, + CallShortMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jshort, + CallShortMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jshort, + CallShortMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jshort, + CallIntMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jint, + CallIntMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jint, + CallIntMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jint, + CallLongMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jlong, + CallLongMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jlong, + CallLongMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jlong, + CallFloatMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jfloat, + CallFloatMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jfloat, + CallFloatMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jfloat, + CallDoubleMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) jdouble, + CallDoubleMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) jdouble, + CallDoubleMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) jdouble, + CallVoidMethod: *const fn (*JNIEnv, jobject, jmethodID, ...) callconv(.C) void, + CallVoidMethodV: *const fn (*JNIEnv, jobject, jmethodID, va_list) callconv(.C) void, + CallVoidMethodA: *const fn (*JNIEnv, jobject, jmethodID, [*c]const jvalue) callconv(.C) void, + CallNonvirtualObjectMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jobject, + CallNonvirtualObjectMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jobject, + CallNonvirtualObjectMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jobject, + CallNonvirtualBooleanMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jboolean, + CallNonvirtualBooleanMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jboolean, + CallNonvirtualBooleanMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jboolean, + CallNonvirtualByteMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jbyte, + CallNonvirtualByteMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jbyte, + CallNonvirtualByteMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jbyte, + CallNonvirtualCharMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jchar, + CallNonvirtualCharMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jchar, + CallNonvirtualCharMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jchar, + CallNonvirtualShortMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jshort, + CallNonvirtualShortMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jshort, + CallNonvirtualShortMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jshort, + CallNonvirtualIntMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jint, + CallNonvirtualIntMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jint, + CallNonvirtualIntMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jint, + CallNonvirtualLongMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jlong, + CallNonvirtualLongMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jlong, + CallNonvirtualLongMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jlong, + CallNonvirtualFloatMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jfloat, + CallNonvirtualFloatMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jfloat, + CallNonvirtualFloatMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jfloat, + CallNonvirtualDoubleMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) jdouble, + CallNonvirtualDoubleMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) jdouble, + CallNonvirtualDoubleMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) jdouble, + CallNonvirtualVoidMethod: *const fn (*JNIEnv, jobject, jclass, jmethodID, ...) callconv(.C) void, + CallNonvirtualVoidMethodV: *const fn (*JNIEnv, jobject, jclass, jmethodID, va_list) callconv(.C) void, + CallNonvirtualVoidMethodA: *const fn (*JNIEnv, jobject, jclass, jmethodID, [*c]const jvalue) callconv(.C) void, + GetFieldID: *const fn (*JNIEnv, jclass, [*:0]const u8, [*:0]const u8) callconv(.C) jfieldID, + GetObjectField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jobject, + GetBooleanField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jboolean, + GetByteField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jbyte, + GetCharField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jchar, + GetShortField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jshort, + GetIntField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jint, + GetLongField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jlong, + GetFloatField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jfloat, + GetDoubleField: *const fn (*JNIEnv, jobject, jfieldID) callconv(.C) jdouble, + SetObjectField: *const fn (*JNIEnv, jobject, jfieldID, jobject) callconv(.C) void, + SetBooleanField: *const fn (*JNIEnv, jobject, jfieldID, jboolean) callconv(.C) void, + SetByteField: *const fn (*JNIEnv, jobject, jfieldID, jbyte) callconv(.C) void, + SetCharField: *const fn (*JNIEnv, jobject, jfieldID, jchar) callconv(.C) void, + SetShortField: *const fn (*JNIEnv, jobject, jfieldID, jshort) callconv(.C) void, + SetIntField: *const fn (*JNIEnv, jobject, jfieldID, jint) callconv(.C) void, + SetLongField: *const fn (*JNIEnv, jobject, jfieldID, jlong) callconv(.C) void, + SetFloatField: *const fn (*JNIEnv, jobject, jfieldID, jfloat) callconv(.C) void, + SetDoubleField: *const fn (*JNIEnv, jobject, jfieldID, jdouble) callconv(.C) void, + GetStaticMethodID: *const fn (*JNIEnv, jclass, [*:0]const u8, [*:0]const u8) callconv(.C) jmethodID, + CallStaticObjectMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jobject, + CallStaticObjectMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jobject, + CallStaticObjectMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jobject, + CallStaticBooleanMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jboolean, + CallStaticBooleanMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jboolean, + CallStaticBooleanMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jboolean, + CallStaticByteMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jbyte, + CallStaticByteMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jbyte, + CallStaticByteMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jbyte, + CallStaticCharMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jchar, + CallStaticCharMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jchar, + CallStaticCharMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jchar, + CallStaticShortMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jshort, + CallStaticShortMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jshort, + CallStaticShortMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jshort, + CallStaticIntMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jint, + CallStaticIntMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jint, + CallStaticIntMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jint, + CallStaticLongMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jlong, + CallStaticLongMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jlong, + CallStaticLongMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jlong, + CallStaticFloatMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jfloat, + CallStaticFloatMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jfloat, + CallStaticFloatMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jfloat, + CallStaticDoubleMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) jdouble, + CallStaticDoubleMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) jdouble, + CallStaticDoubleMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) jdouble, + CallStaticVoidMethod: *const fn (*JNIEnv, jclass, jmethodID, ...) callconv(.C) void, + CallStaticVoidMethodV: *const fn (*JNIEnv, jclass, jmethodID, va_list) callconv(.C) void, + CallStaticVoidMethodA: *const fn (*JNIEnv, jclass, jmethodID, [*c]const jvalue) callconv(.C) void, + GetStaticFieldID: *const fn (*JNIEnv, jclass, [*:0]const u8, [*:0]const u8) callconv(.C) jfieldID, + GetStaticObjectField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jobject, + GetStaticBooleanField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jboolean, + GetStaticByteField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jbyte, + GetStaticCharField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jchar, + GetStaticShortField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jshort, + GetStaticIntField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jint, + GetStaticLongField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jlong, + GetStaticFloatField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jfloat, + GetStaticDoubleField: *const fn (*JNIEnv, jclass, jfieldID) callconv(.C) jdouble, + SetStaticObjectField: *const fn (*JNIEnv, jclass, jfieldID, jobject) callconv(.C) void, + SetStaticBooleanField: *const fn (*JNIEnv, jclass, jfieldID, jboolean) callconv(.C) void, + SetStaticByteField: *const fn (*JNIEnv, jclass, jfieldID, jbyte) callconv(.C) void, + SetStaticCharField: *const fn (*JNIEnv, jclass, jfieldID, jchar) callconv(.C) void, + SetStaticShortField: *const fn (*JNIEnv, jclass, jfieldID, jshort) callconv(.C) void, + SetStaticIntField: *const fn (*JNIEnv, jclass, jfieldID, jint) callconv(.C) void, + SetStaticLongField: *const fn (*JNIEnv, jclass, jfieldID, jlong) callconv(.C) void, + SetStaticFloatField: *const fn (*JNIEnv, jclass, jfieldID, jfloat) callconv(.C) void, + SetStaticDoubleField: *const fn (*JNIEnv, jclass, jfieldID, jdouble) callconv(.C) void, + NewString: *const fn (*JNIEnv, [*c]const jchar, jsize) callconv(.C) jstring, + GetStringLength: *const fn (*JNIEnv, jstring) callconv(.C) jsize, + GetStringChars: *const fn (*JNIEnv, jstring, [*c]jboolean) callconv(.C) [*c]const jchar, + ReleaseStringChars: *const fn (*JNIEnv, jstring, [*c]const jchar) callconv(.C) void, + NewStringUTF: *const fn (*JNIEnv, [*:0]const u8) callconv(.C) jstring, + GetStringUTFLength: *const fn (*JNIEnv, jstring) callconv(.C) jsize, + GetStringUTFChars: *const fn (*JNIEnv, jstring, [*c]jboolean) callconv(.C) [*:0]const u8, + ReleaseStringUTFChars: *const fn (*JNIEnv, jstring, [*:0]const u8) callconv(.C) void, + GetArrayLength: *const fn (*JNIEnv, jarray) callconv(.C) jsize, + NewObjectArray: *const fn (*JNIEnv, jsize, jclass, jobject) callconv(.C) jobjectArray, + GetObjectArrayElement: *const fn (*JNIEnv, jobjectArray, jsize) callconv(.C) jobject, + SetObjectArrayElement: *const fn (*JNIEnv, jobjectArray, jsize, jobject) callconv(.C) void, + NewBooleanArray: *const fn (*JNIEnv, jsize) callconv(.C) jbooleanArray, + NewByteArray: *const fn (*JNIEnv, jsize) callconv(.C) jbyteArray, + NewCharArray: *const fn (*JNIEnv, jsize) callconv(.C) jcharArray, + NewShortArray: *const fn (*JNIEnv, jsize) callconv(.C) jshortArray, + NewIntArray: *const fn (*JNIEnv, jsize) callconv(.C) jintArray, + NewLongArray: *const fn (*JNIEnv, jsize) callconv(.C) jlongArray, + NewFloatArray: *const fn (*JNIEnv, jsize) callconv(.C) jfloatArray, + NewDoubleArray: *const fn (*JNIEnv, jsize) callconv(.C) jdoubleArray, + GetBooleanArrayElements: *const fn (*JNIEnv, jbooleanArray, [*c]jboolean) callconv(.C) [*c]jboolean, + GetByteArrayElements: *const fn (*JNIEnv, jbyteArray, [*c]jboolean) callconv(.C) [*c]jbyte, + GetCharArrayElements: *const fn (*JNIEnv, jcharArray, [*c]jboolean) callconv(.C) [*c]jchar, + GetShortArrayElements: *const fn (*JNIEnv, jshortArray, [*c]jboolean) callconv(.C) [*c]jshort, + GetIntArrayElements: *const fn (*JNIEnv, jintArray, [*c]jboolean) callconv(.C) [*c]jint, + GetLongArrayElements: *const fn (*JNIEnv, jlongArray, [*c]jboolean) callconv(.C) [*c]jlong, + GetFloatArrayElements: *const fn (*JNIEnv, jfloatArray, [*c]jboolean) callconv(.C) [*c]jfloat, + GetDoubleArrayElements: *const fn (*JNIEnv, jdoubleArray, [*c]jboolean) callconv(.C) [*c]jdouble, + ReleaseBooleanArrayElements: *const fn (*JNIEnv, jbooleanArray, [*c]jboolean, jint) callconv(.C) void, + ReleaseByteArrayElements: *const fn (*JNIEnv, jbyteArray, [*c]jbyte, jint) callconv(.C) void, + ReleaseCharArrayElements: *const fn (*JNIEnv, jcharArray, [*c]jchar, jint) callconv(.C) void, + ReleaseShortArrayElements: *const fn (*JNIEnv, jshortArray, [*c]jshort, jint) callconv(.C) void, + ReleaseIntArrayElements: *const fn (*JNIEnv, jintArray, [*c]jint, jint) callconv(.C) void, + ReleaseLongArrayElements: *const fn (*JNIEnv, jlongArray, [*c]jlong, jint) callconv(.C) void, + ReleaseFloatArrayElements: *const fn (*JNIEnv, jfloatArray, [*c]jfloat, jint) callconv(.C) void, + ReleaseDoubleArrayElements: *const fn (*JNIEnv, jdoubleArray, [*c]jdouble, jint) callconv(.C) void, + GetBooleanArrayRegion: *const fn (*JNIEnv, jbooleanArray, jsize, jsize, [*c]jboolean) callconv(.C) void, + GetByteArrayRegion: *const fn (*JNIEnv, jbyteArray, jsize, jsize, [*c]jbyte) callconv(.C) void, + GetCharArrayRegion: *const fn (*JNIEnv, jcharArray, jsize, jsize, [*c]jchar) callconv(.C) void, + GetShortArrayRegion: *const fn (*JNIEnv, jshortArray, jsize, jsize, [*c]jshort) callconv(.C) void, + GetIntArrayRegion: *const fn (*JNIEnv, jintArray, jsize, jsize, [*c]jint) callconv(.C) void, + GetLongArrayRegion: *const fn (*JNIEnv, jlongArray, jsize, jsize, [*c]jlong) callconv(.C) void, + GetFloatArrayRegion: *const fn (*JNIEnv, jfloatArray, jsize, jsize, [*c]jfloat) callconv(.C) void, + GetDoubleArrayRegion: *const fn (*JNIEnv, jdoubleArray, jsize, jsize, [*c]jdouble) callconv(.C) void, + SetBooleanArrayRegion: *const fn (*JNIEnv, jbooleanArray, jsize, jsize, [*c]const jboolean) callconv(.C) void, + SetByteArrayRegion: *const fn (*JNIEnv, jbyteArray, jsize, jsize, [*c]const jbyte) callconv(.C) void, + SetCharArrayRegion: *const fn (*JNIEnv, jcharArray, jsize, jsize, [*c]const jchar) callconv(.C) void, + SetShortArrayRegion: *const fn (*JNIEnv, jshortArray, jsize, jsize, [*c]const jshort) callconv(.C) void, + SetIntArrayRegion: *const fn (*JNIEnv, jintArray, jsize, jsize, [*c]const jint) callconv(.C) void, + SetLongArrayRegion: *const fn (*JNIEnv, jlongArray, jsize, jsize, [*c]const jlong) callconv(.C) void, + SetFloatArrayRegion: *const fn (*JNIEnv, jfloatArray, jsize, jsize, [*c]const jfloat) callconv(.C) void, + SetDoubleArrayRegion: *const fn (*JNIEnv, jdoubleArray, jsize, jsize, [*c]const jdouble) callconv(.C) void, + RegisterNatives: *const fn (*JNIEnv, jclass, [*c]const JNINativeMethod, jint) callconv(.C) jint, + UnregisterNatives: *const fn (*JNIEnv, jclass) callconv(.C) jint, + MonitorEnter: *const fn (*JNIEnv, jobject) callconv(.C) jint, + MonitorExit: *const fn (*JNIEnv, jobject) callconv(.C) jint, + GetJavaVM: *const fn (*JNIEnv, [*c][*c]JavaVM) callconv(.C) jint, + GetStringRegion: *const fn (*JNIEnv, jstring, jsize, jsize, [*c]jchar) callconv(.C) void, + GetStringUTFRegion: *const fn (*JNIEnv, jstring, jsize, jsize, [*c]u8) callconv(.C) void, + GetPrimitiveArrayCritical: *const fn (*JNIEnv, jarray, [*c]jboolean) callconv(.C) ?*anyopaque, + ReleasePrimitiveArrayCritical: *const fn (*JNIEnv, jarray, ?*anyopaque, jint) callconv(.C) void, + GetStringCritical: *const fn (*JNIEnv, jstring, [*c]jboolean) callconv(.C) [*c]const jchar, + ReleaseStringCritical: *const fn (*JNIEnv, jstring, [*c]const jchar) callconv(.C) void, + NewWeakGlobalRef: *const fn (*JNIEnv, jobject) callconv(.C) jweak, + DeleteWeakGlobalRef: *const fn (*JNIEnv, jweak) callconv(.C) void, + ExceptionCheck: *const fn (*JNIEnv) callconv(.C) jboolean, + NewDirectByteBuffer: *const fn (*JNIEnv, ?*anyopaque, jlong) callconv(.C) jobject, + GetDirectBufferAddress: *const fn (*JNIEnv, jobject) callconv(.C) ?*anyopaque, + GetDirectBufferCapacity: *const fn (*JNIEnv, jobject) callconv(.C) jlong, + GetObjectRefType: *const fn (*JNIEnv, jobject) callconv(.C) jobjectRefType, +}; +pub const struct__JNIEnv = extern struct { + functions: [*c]const JNINativeInterface, +}; +pub const struct__JavaVM = extern struct { + functions: [*c]const struct_JNIInvokeInterface, +}; +pub const C_JNIEnv = *const JNINativeInterface; +pub const JNIEnv = *const JNINativeInterface; +pub const JavaVM = *const struct_JNIInvokeInterface; +pub const struct_JavaVMAttachArgs = extern struct { + version: jint, + name: [*c]const u8, + group: jobject, +}; +pub const JavaVMAttachArgs = struct_JavaVMAttachArgs; +pub const struct_JavaVMOption = extern struct { + optionString: [*c]const u8, + extraInfo: ?*anyopaque, +}; +pub const JavaVMOption = struct_JavaVMOption; +pub const struct_JavaVMInitArgs = extern struct { + version: jint, + nOptions: jint, + options: [*c]JavaVMOption, + ignoreUnrecognized: jboolean, +}; +pub const JavaVMInitArgs = struct_JavaVMInitArgs; +pub extern fn JNI_GetDefaultJavaVMInitArgs(?*anyopaque) jint; +pub extern fn JNI_CreateJavaVM([*c][*c]JavaVM, [*c][*c]JNIEnv, ?*anyopaque) jint; +pub extern fn JNI_GetCreatedJavaVMs([*c][*c]JavaVM, jsize, [*c]jsize) jint; +pub extern fn JNI_OnLoad(vm: [*c]JavaVM, reserved: ?*anyopaque) jint; +pub extern fn JNI_OnUnload(vm: [*c]JavaVM, reserved: ?*anyopaque) void; +pub const AKEYCODE_UNKNOWN = @enumToInt(enum_unnamed_16.AKEYCODE_UNKNOWN); +pub const AKEYCODE_SOFT_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_SOFT_LEFT); +pub const AKEYCODE_SOFT_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_SOFT_RIGHT); +pub const AKEYCODE_HOME = @enumToInt(enum_unnamed_16.AKEYCODE_HOME); +pub const AKEYCODE_BACK = @enumToInt(enum_unnamed_16.AKEYCODE_BACK); +pub const AKEYCODE_CALL = @enumToInt(enum_unnamed_16.AKEYCODE_CALL); +pub const AKEYCODE_ENDCALL = @enumToInt(enum_unnamed_16.AKEYCODE_ENDCALL); +pub const AKEYCODE_0 = @enumToInt(enum_unnamed_16.AKEYCODE_0); +pub const AKEYCODE_1 = @enumToInt(enum_unnamed_16.AKEYCODE_1); +pub const AKEYCODE_2 = @enumToInt(enum_unnamed_16.AKEYCODE_2); +pub const AKEYCODE_3 = @enumToInt(enum_unnamed_16.AKEYCODE_3); +pub const AKEYCODE_4 = @enumToInt(enum_unnamed_16.AKEYCODE_4); +pub const AKEYCODE_5 = @enumToInt(enum_unnamed_16.AKEYCODE_5); +pub const AKEYCODE_6 = @enumToInt(enum_unnamed_16.AKEYCODE_6); +pub const AKEYCODE_7 = @enumToInt(enum_unnamed_16.AKEYCODE_7); +pub const AKEYCODE_8 = @enumToInt(enum_unnamed_16.AKEYCODE_8); +pub const AKEYCODE_9 = @enumToInt(enum_unnamed_16.AKEYCODE_9); +pub const AKEYCODE_STAR = @enumToInt(enum_unnamed_16.AKEYCODE_STAR); +pub const AKEYCODE_POUND = @enumToInt(enum_unnamed_16.AKEYCODE_POUND); +pub const AKEYCODE_DPAD_UP = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_UP); +pub const AKEYCODE_DPAD_DOWN = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_DOWN); +pub const AKEYCODE_DPAD_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_LEFT); +pub const AKEYCODE_DPAD_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_RIGHT); +pub const AKEYCODE_DPAD_CENTER = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_CENTER); +pub const AKEYCODE_VOLUME_UP = @enumToInt(enum_unnamed_16.AKEYCODE_VOLUME_UP); +pub const AKEYCODE_VOLUME_DOWN = @enumToInt(enum_unnamed_16.AKEYCODE_VOLUME_DOWN); +pub const AKEYCODE_POWER = @enumToInt(enum_unnamed_16.AKEYCODE_POWER); +pub const AKEYCODE_CAMERA = @enumToInt(enum_unnamed_16.AKEYCODE_CAMERA); +pub const AKEYCODE_CLEAR = @enumToInt(enum_unnamed_16.AKEYCODE_CLEAR); +pub const AKEYCODE_A = @enumToInt(enum_unnamed_16.AKEYCODE_A); +pub const AKEYCODE_B = @enumToInt(enum_unnamed_16.AKEYCODE_B); +pub const AKEYCODE_C = @enumToInt(enum_unnamed_16.AKEYCODE_C); +pub const AKEYCODE_D = @enumToInt(enum_unnamed_16.AKEYCODE_D); +pub const AKEYCODE_E = @enumToInt(enum_unnamed_16.AKEYCODE_E); +pub const AKEYCODE_F = @enumToInt(enum_unnamed_16.AKEYCODE_F); +pub const AKEYCODE_G = @enumToInt(enum_unnamed_16.AKEYCODE_G); +pub const AKEYCODE_H = @enumToInt(enum_unnamed_16.AKEYCODE_H); +pub const AKEYCODE_I = @enumToInt(enum_unnamed_16.AKEYCODE_I); +pub const AKEYCODE_J = @enumToInt(enum_unnamed_16.AKEYCODE_J); +pub const AKEYCODE_K = @enumToInt(enum_unnamed_16.AKEYCODE_K); +pub const AKEYCODE_L = @enumToInt(enum_unnamed_16.AKEYCODE_L); +pub const AKEYCODE_M = @enumToInt(enum_unnamed_16.AKEYCODE_M); +pub const AKEYCODE_N = @enumToInt(enum_unnamed_16.AKEYCODE_N); +pub const AKEYCODE_O = @enumToInt(enum_unnamed_16.AKEYCODE_O); +pub const AKEYCODE_P = @enumToInt(enum_unnamed_16.AKEYCODE_P); +pub const AKEYCODE_Q = @enumToInt(enum_unnamed_16.AKEYCODE_Q); +pub const AKEYCODE_R = @enumToInt(enum_unnamed_16.AKEYCODE_R); +pub const AKEYCODE_S = @enumToInt(enum_unnamed_16.AKEYCODE_S); +pub const AKEYCODE_T = @enumToInt(enum_unnamed_16.AKEYCODE_T); +pub const AKEYCODE_U = @enumToInt(enum_unnamed_16.AKEYCODE_U); +pub const AKEYCODE_V = @enumToInt(enum_unnamed_16.AKEYCODE_V); +pub const AKEYCODE_W = @enumToInt(enum_unnamed_16.AKEYCODE_W); +pub const AKEYCODE_X = @enumToInt(enum_unnamed_16.AKEYCODE_X); +pub const AKEYCODE_Y = @enumToInt(enum_unnamed_16.AKEYCODE_Y); +pub const AKEYCODE_Z = @enumToInt(enum_unnamed_16.AKEYCODE_Z); +pub const AKEYCODE_COMMA = @enumToInt(enum_unnamed_16.AKEYCODE_COMMA); +pub const AKEYCODE_PERIOD = @enumToInt(enum_unnamed_16.AKEYCODE_PERIOD); +pub const AKEYCODE_ALT_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_ALT_LEFT); +pub const AKEYCODE_ALT_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_ALT_RIGHT); +pub const AKEYCODE_SHIFT_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_SHIFT_LEFT); +pub const AKEYCODE_SHIFT_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_SHIFT_RIGHT); +pub const AKEYCODE_TAB = @enumToInt(enum_unnamed_16.AKEYCODE_TAB); +pub const AKEYCODE_SPACE = @enumToInt(enum_unnamed_16.AKEYCODE_SPACE); +pub const AKEYCODE_SYM = @enumToInt(enum_unnamed_16.AKEYCODE_SYM); +pub const AKEYCODE_EXPLORER = @enumToInt(enum_unnamed_16.AKEYCODE_EXPLORER); +pub const AKEYCODE_ENVELOPE = @enumToInt(enum_unnamed_16.AKEYCODE_ENVELOPE); +pub const AKEYCODE_ENTER = @enumToInt(enum_unnamed_16.AKEYCODE_ENTER); +pub const AKEYCODE_DEL = @enumToInt(enum_unnamed_16.AKEYCODE_DEL); +pub const AKEYCODE_GRAVE = @enumToInt(enum_unnamed_16.AKEYCODE_GRAVE); +pub const AKEYCODE_MINUS = @enumToInt(enum_unnamed_16.AKEYCODE_MINUS); +pub const AKEYCODE_EQUALS = @enumToInt(enum_unnamed_16.AKEYCODE_EQUALS); +pub const AKEYCODE_LEFT_BRACKET = @enumToInt(enum_unnamed_16.AKEYCODE_LEFT_BRACKET); +pub const AKEYCODE_RIGHT_BRACKET = @enumToInt(enum_unnamed_16.AKEYCODE_RIGHT_BRACKET); +pub const AKEYCODE_BACKSLASH = @enumToInt(enum_unnamed_16.AKEYCODE_BACKSLASH); +pub const AKEYCODE_SEMICOLON = @enumToInt(enum_unnamed_16.AKEYCODE_SEMICOLON); +pub const AKEYCODE_APOSTROPHE = @enumToInt(enum_unnamed_16.AKEYCODE_APOSTROPHE); +pub const AKEYCODE_SLASH = @enumToInt(enum_unnamed_16.AKEYCODE_SLASH); +pub const AKEYCODE_AT = @enumToInt(enum_unnamed_16.AKEYCODE_AT); +pub const AKEYCODE_NUM = @enumToInt(enum_unnamed_16.AKEYCODE_NUM); +pub const AKEYCODE_HEADSETHOOK = @enumToInt(enum_unnamed_16.AKEYCODE_HEADSETHOOK); +pub const AKEYCODE_FOCUS = @enumToInt(enum_unnamed_16.AKEYCODE_FOCUS); +pub const AKEYCODE_PLUS = @enumToInt(enum_unnamed_16.AKEYCODE_PLUS); +pub const AKEYCODE_MENU = @enumToInt(enum_unnamed_16.AKEYCODE_MENU); +pub const AKEYCODE_NOTIFICATION = @enumToInt(enum_unnamed_16.AKEYCODE_NOTIFICATION); +pub const AKEYCODE_SEARCH = @enumToInt(enum_unnamed_16.AKEYCODE_SEARCH); +pub const AKEYCODE_MEDIA_PLAY_PAUSE = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_PLAY_PAUSE); +pub const AKEYCODE_MEDIA_STOP = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_STOP); +pub const AKEYCODE_MEDIA_NEXT = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_NEXT); +pub const AKEYCODE_MEDIA_PREVIOUS = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_PREVIOUS); +pub const AKEYCODE_MEDIA_REWIND = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_REWIND); +pub const AKEYCODE_MEDIA_FAST_FORWARD = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_FAST_FORWARD); +pub const AKEYCODE_MUTE = @enumToInt(enum_unnamed_16.AKEYCODE_MUTE); +pub const AKEYCODE_PAGE_UP = @enumToInt(enum_unnamed_16.AKEYCODE_PAGE_UP); +pub const AKEYCODE_PAGE_DOWN = @enumToInt(enum_unnamed_16.AKEYCODE_PAGE_DOWN); +pub const AKEYCODE_PICTSYMBOLS = @enumToInt(enum_unnamed_16.AKEYCODE_PICTSYMBOLS); +pub const AKEYCODE_SWITCH_CHARSET = @enumToInt(enum_unnamed_16.AKEYCODE_SWITCH_CHARSET); +pub const AKEYCODE_BUTTON_A = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_A); +pub const AKEYCODE_BUTTON_B = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_B); +pub const AKEYCODE_BUTTON_C = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_C); +pub const AKEYCODE_BUTTON_X = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_X); +pub const AKEYCODE_BUTTON_Y = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_Y); +pub const AKEYCODE_BUTTON_Z = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_Z); +pub const AKEYCODE_BUTTON_L1 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_L1); +pub const AKEYCODE_BUTTON_R1 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_R1); +pub const AKEYCODE_BUTTON_L2 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_L2); +pub const AKEYCODE_BUTTON_R2 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_R2); +pub const AKEYCODE_BUTTON_THUMBL = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_THUMBL); +pub const AKEYCODE_BUTTON_THUMBR = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_THUMBR); +pub const AKEYCODE_BUTTON_START = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_START); +pub const AKEYCODE_BUTTON_SELECT = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_SELECT); +pub const AKEYCODE_BUTTON_MODE = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_MODE); +pub const AKEYCODE_ESCAPE = @enumToInt(enum_unnamed_16.AKEYCODE_ESCAPE); +pub const AKEYCODE_FORWARD_DEL = @enumToInt(enum_unnamed_16.AKEYCODE_FORWARD_DEL); +pub const AKEYCODE_CTRL_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_CTRL_LEFT); +pub const AKEYCODE_CTRL_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_CTRL_RIGHT); +pub const AKEYCODE_CAPS_LOCK = @enumToInt(enum_unnamed_16.AKEYCODE_CAPS_LOCK); +pub const AKEYCODE_SCROLL_LOCK = @enumToInt(enum_unnamed_16.AKEYCODE_SCROLL_LOCK); +pub const AKEYCODE_META_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_META_LEFT); +pub const AKEYCODE_META_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_META_RIGHT); +pub const AKEYCODE_FUNCTION = @enumToInt(enum_unnamed_16.AKEYCODE_FUNCTION); +pub const AKEYCODE_SYSRQ = @enumToInt(enum_unnamed_16.AKEYCODE_SYSRQ); +pub const AKEYCODE_BREAK = @enumToInt(enum_unnamed_16.AKEYCODE_BREAK); +pub const AKEYCODE_MOVE_HOME = @enumToInt(enum_unnamed_16.AKEYCODE_MOVE_HOME); +pub const AKEYCODE_MOVE_END = @enumToInt(enum_unnamed_16.AKEYCODE_MOVE_END); +pub const AKEYCODE_INSERT = @enumToInt(enum_unnamed_16.AKEYCODE_INSERT); +pub const AKEYCODE_FORWARD = @enumToInt(enum_unnamed_16.AKEYCODE_FORWARD); +pub const AKEYCODE_MEDIA_PLAY = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_PLAY); +pub const AKEYCODE_MEDIA_PAUSE = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_PAUSE); +pub const AKEYCODE_MEDIA_CLOSE = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_CLOSE); +pub const AKEYCODE_MEDIA_EJECT = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_EJECT); +pub const AKEYCODE_MEDIA_RECORD = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_RECORD); +pub const AKEYCODE_F1 = @enumToInt(enum_unnamed_16.AKEYCODE_F1); +pub const AKEYCODE_F2 = @enumToInt(enum_unnamed_16.AKEYCODE_F2); +pub const AKEYCODE_F3 = @enumToInt(enum_unnamed_16.AKEYCODE_F3); +pub const AKEYCODE_F4 = @enumToInt(enum_unnamed_16.AKEYCODE_F4); +pub const AKEYCODE_F5 = @enumToInt(enum_unnamed_16.AKEYCODE_F5); +pub const AKEYCODE_F6 = @enumToInt(enum_unnamed_16.AKEYCODE_F6); +pub const AKEYCODE_F7 = @enumToInt(enum_unnamed_16.AKEYCODE_F7); +pub const AKEYCODE_F8 = @enumToInt(enum_unnamed_16.AKEYCODE_F8); +pub const AKEYCODE_F9 = @enumToInt(enum_unnamed_16.AKEYCODE_F9); +pub const AKEYCODE_F10 = @enumToInt(enum_unnamed_16.AKEYCODE_F10); +pub const AKEYCODE_F11 = @enumToInt(enum_unnamed_16.AKEYCODE_F11); +pub const AKEYCODE_F12 = @enumToInt(enum_unnamed_16.AKEYCODE_F12); +pub const AKEYCODE_NUM_LOCK = @enumToInt(enum_unnamed_16.AKEYCODE_NUM_LOCK); +pub const AKEYCODE_NUMPAD_0 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_0); +pub const AKEYCODE_NUMPAD_1 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_1); +pub const AKEYCODE_NUMPAD_2 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_2); +pub const AKEYCODE_NUMPAD_3 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_3); +pub const AKEYCODE_NUMPAD_4 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_4); +pub const AKEYCODE_NUMPAD_5 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_5); +pub const AKEYCODE_NUMPAD_6 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_6); +pub const AKEYCODE_NUMPAD_7 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_7); +pub const AKEYCODE_NUMPAD_8 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_8); +pub const AKEYCODE_NUMPAD_9 = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_9); +pub const AKEYCODE_NUMPAD_DIVIDE = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_DIVIDE); +pub const AKEYCODE_NUMPAD_MULTIPLY = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_MULTIPLY); +pub const AKEYCODE_NUMPAD_SUBTRACT = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_SUBTRACT); +pub const AKEYCODE_NUMPAD_ADD = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_ADD); +pub const AKEYCODE_NUMPAD_DOT = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_DOT); +pub const AKEYCODE_NUMPAD_COMMA = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_COMMA); +pub const AKEYCODE_NUMPAD_ENTER = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_ENTER); +pub const AKEYCODE_NUMPAD_EQUALS = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_EQUALS); +pub const AKEYCODE_NUMPAD_LEFT_PAREN = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_LEFT_PAREN); +pub const AKEYCODE_NUMPAD_RIGHT_PAREN = @enumToInt(enum_unnamed_16.AKEYCODE_NUMPAD_RIGHT_PAREN); +pub const AKEYCODE_VOLUME_MUTE = @enumToInt(enum_unnamed_16.AKEYCODE_VOLUME_MUTE); +pub const AKEYCODE_INFO = @enumToInt(enum_unnamed_16.AKEYCODE_INFO); +pub const AKEYCODE_CHANNEL_UP = @enumToInt(enum_unnamed_16.AKEYCODE_CHANNEL_UP); +pub const AKEYCODE_CHANNEL_DOWN = @enumToInt(enum_unnamed_16.AKEYCODE_CHANNEL_DOWN); +pub const AKEYCODE_ZOOM_IN = @enumToInt(enum_unnamed_16.AKEYCODE_ZOOM_IN); +pub const AKEYCODE_ZOOM_OUT = @enumToInt(enum_unnamed_16.AKEYCODE_ZOOM_OUT); +pub const AKEYCODE_TV = @enumToInt(enum_unnamed_16.AKEYCODE_TV); +pub const AKEYCODE_WINDOW = @enumToInt(enum_unnamed_16.AKEYCODE_WINDOW); +pub const AKEYCODE_GUIDE = @enumToInt(enum_unnamed_16.AKEYCODE_GUIDE); +pub const AKEYCODE_DVR = @enumToInt(enum_unnamed_16.AKEYCODE_DVR); +pub const AKEYCODE_BOOKMARK = @enumToInt(enum_unnamed_16.AKEYCODE_BOOKMARK); +pub const AKEYCODE_CAPTIONS = @enumToInt(enum_unnamed_16.AKEYCODE_CAPTIONS); +pub const AKEYCODE_SETTINGS = @enumToInt(enum_unnamed_16.AKEYCODE_SETTINGS); +pub const AKEYCODE_TV_POWER = @enumToInt(enum_unnamed_16.AKEYCODE_TV_POWER); +pub const AKEYCODE_TV_INPUT = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT); +pub const AKEYCODE_STB_POWER = @enumToInt(enum_unnamed_16.AKEYCODE_STB_POWER); +pub const AKEYCODE_STB_INPUT = @enumToInt(enum_unnamed_16.AKEYCODE_STB_INPUT); +pub const AKEYCODE_AVR_POWER = @enumToInt(enum_unnamed_16.AKEYCODE_AVR_POWER); +pub const AKEYCODE_AVR_INPUT = @enumToInt(enum_unnamed_16.AKEYCODE_AVR_INPUT); +pub const AKEYCODE_PROG_RED = @enumToInt(enum_unnamed_16.AKEYCODE_PROG_RED); +pub const AKEYCODE_PROG_GREEN = @enumToInt(enum_unnamed_16.AKEYCODE_PROG_GREEN); +pub const AKEYCODE_PROG_YELLOW = @enumToInt(enum_unnamed_16.AKEYCODE_PROG_YELLOW); +pub const AKEYCODE_PROG_BLUE = @enumToInt(enum_unnamed_16.AKEYCODE_PROG_BLUE); +pub const AKEYCODE_APP_SWITCH = @enumToInt(enum_unnamed_16.AKEYCODE_APP_SWITCH); +pub const AKEYCODE_BUTTON_1 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_1); +pub const AKEYCODE_BUTTON_2 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_2); +pub const AKEYCODE_BUTTON_3 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_3); +pub const AKEYCODE_BUTTON_4 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_4); +pub const AKEYCODE_BUTTON_5 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_5); +pub const AKEYCODE_BUTTON_6 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_6); +pub const AKEYCODE_BUTTON_7 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_7); +pub const AKEYCODE_BUTTON_8 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_8); +pub const AKEYCODE_BUTTON_9 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_9); +pub const AKEYCODE_BUTTON_10 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_10); +pub const AKEYCODE_BUTTON_11 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_11); +pub const AKEYCODE_BUTTON_12 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_12); +pub const AKEYCODE_BUTTON_13 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_13); +pub const AKEYCODE_BUTTON_14 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_14); +pub const AKEYCODE_BUTTON_15 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_15); +pub const AKEYCODE_BUTTON_16 = @enumToInt(enum_unnamed_16.AKEYCODE_BUTTON_16); +pub const AKEYCODE_LANGUAGE_SWITCH = @enumToInt(enum_unnamed_16.AKEYCODE_LANGUAGE_SWITCH); +pub const AKEYCODE_MANNER_MODE = @enumToInt(enum_unnamed_16.AKEYCODE_MANNER_MODE); +pub const AKEYCODE_3D_MODE = @enumToInt(enum_unnamed_16.AKEYCODE_3D_MODE); +pub const AKEYCODE_CONTACTS = @enumToInt(enum_unnamed_16.AKEYCODE_CONTACTS); +pub const AKEYCODE_CALENDAR = @enumToInt(enum_unnamed_16.AKEYCODE_CALENDAR); +pub const AKEYCODE_MUSIC = @enumToInt(enum_unnamed_16.AKEYCODE_MUSIC); +pub const AKEYCODE_CALCULATOR = @enumToInt(enum_unnamed_16.AKEYCODE_CALCULATOR); +pub const AKEYCODE_ZENKAKU_HANKAKU = @enumToInt(enum_unnamed_16.AKEYCODE_ZENKAKU_HANKAKU); +pub const AKEYCODE_EISU = @enumToInt(enum_unnamed_16.AKEYCODE_EISU); +pub const AKEYCODE_MUHENKAN = @enumToInt(enum_unnamed_16.AKEYCODE_MUHENKAN); +pub const AKEYCODE_HENKAN = @enumToInt(enum_unnamed_16.AKEYCODE_HENKAN); +pub const AKEYCODE_KATAKANA_HIRAGANA = @enumToInt(enum_unnamed_16.AKEYCODE_KATAKANA_HIRAGANA); +pub const AKEYCODE_YEN = @enumToInt(enum_unnamed_16.AKEYCODE_YEN); +pub const AKEYCODE_RO = @enumToInt(enum_unnamed_16.AKEYCODE_RO); +pub const AKEYCODE_KANA = @enumToInt(enum_unnamed_16.AKEYCODE_KANA); +pub const AKEYCODE_ASSIST = @enumToInt(enum_unnamed_16.AKEYCODE_ASSIST); +pub const AKEYCODE_BRIGHTNESS_DOWN = @enumToInt(enum_unnamed_16.AKEYCODE_BRIGHTNESS_DOWN); +pub const AKEYCODE_BRIGHTNESS_UP = @enumToInt(enum_unnamed_16.AKEYCODE_BRIGHTNESS_UP); +pub const AKEYCODE_MEDIA_AUDIO_TRACK = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_AUDIO_TRACK); +pub const AKEYCODE_SLEEP = @enumToInt(enum_unnamed_16.AKEYCODE_SLEEP); +pub const AKEYCODE_WAKEUP = @enumToInt(enum_unnamed_16.AKEYCODE_WAKEUP); +pub const AKEYCODE_PAIRING = @enumToInt(enum_unnamed_16.AKEYCODE_PAIRING); +pub const AKEYCODE_MEDIA_TOP_MENU = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_TOP_MENU); +pub const AKEYCODE_11 = @enumToInt(enum_unnamed_16.AKEYCODE_11); +pub const AKEYCODE_12 = @enumToInt(enum_unnamed_16.AKEYCODE_12); +pub const AKEYCODE_LAST_CHANNEL = @enumToInt(enum_unnamed_16.AKEYCODE_LAST_CHANNEL); +pub const AKEYCODE_TV_DATA_SERVICE = @enumToInt(enum_unnamed_16.AKEYCODE_TV_DATA_SERVICE); +pub const AKEYCODE_VOICE_ASSIST = @enumToInt(enum_unnamed_16.AKEYCODE_VOICE_ASSIST); +pub const AKEYCODE_TV_RADIO_SERVICE = @enumToInt(enum_unnamed_16.AKEYCODE_TV_RADIO_SERVICE); +pub const AKEYCODE_TV_TELETEXT = @enumToInt(enum_unnamed_16.AKEYCODE_TV_TELETEXT); +pub const AKEYCODE_TV_NUMBER_ENTRY = @enumToInt(enum_unnamed_16.AKEYCODE_TV_NUMBER_ENTRY); +pub const AKEYCODE_TV_TERRESTRIAL_ANALOG = @enumToInt(enum_unnamed_16.AKEYCODE_TV_TERRESTRIAL_ANALOG); +pub const AKEYCODE_TV_TERRESTRIAL_DIGITAL = @enumToInt(enum_unnamed_16.AKEYCODE_TV_TERRESTRIAL_DIGITAL); +pub const AKEYCODE_TV_SATELLITE = @enumToInt(enum_unnamed_16.AKEYCODE_TV_SATELLITE); +pub const AKEYCODE_TV_SATELLITE_BS = @enumToInt(enum_unnamed_16.AKEYCODE_TV_SATELLITE_BS); +pub const AKEYCODE_TV_SATELLITE_CS = @enumToInt(enum_unnamed_16.AKEYCODE_TV_SATELLITE_CS); +pub const AKEYCODE_TV_SATELLITE_SERVICE = @enumToInt(enum_unnamed_16.AKEYCODE_TV_SATELLITE_SERVICE); +pub const AKEYCODE_TV_NETWORK = @enumToInt(enum_unnamed_16.AKEYCODE_TV_NETWORK); +pub const AKEYCODE_TV_ANTENNA_CABLE = @enumToInt(enum_unnamed_16.AKEYCODE_TV_ANTENNA_CABLE); +pub const AKEYCODE_TV_INPUT_HDMI_1 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_HDMI_1); +pub const AKEYCODE_TV_INPUT_HDMI_2 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_HDMI_2); +pub const AKEYCODE_TV_INPUT_HDMI_3 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_HDMI_3); +pub const AKEYCODE_TV_INPUT_HDMI_4 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_HDMI_4); +pub const AKEYCODE_TV_INPUT_COMPOSITE_1 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_COMPOSITE_1); +pub const AKEYCODE_TV_INPUT_COMPOSITE_2 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_COMPOSITE_2); +pub const AKEYCODE_TV_INPUT_COMPONENT_1 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_COMPONENT_1); +pub const AKEYCODE_TV_INPUT_COMPONENT_2 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_COMPONENT_2); +pub const AKEYCODE_TV_INPUT_VGA_1 = @enumToInt(enum_unnamed_16.AKEYCODE_TV_INPUT_VGA_1); +pub const AKEYCODE_TV_AUDIO_DESCRIPTION = @enumToInt(enum_unnamed_16.AKEYCODE_TV_AUDIO_DESCRIPTION); +pub const AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP = @enumToInt(enum_unnamed_16.AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP); +pub const AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN = @enumToInt(enum_unnamed_16.AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN); +pub const AKEYCODE_TV_ZOOM_MODE = @enumToInt(enum_unnamed_16.AKEYCODE_TV_ZOOM_MODE); +pub const AKEYCODE_TV_CONTENTS_MENU = @enumToInt(enum_unnamed_16.AKEYCODE_TV_CONTENTS_MENU); +pub const AKEYCODE_TV_MEDIA_CONTEXT_MENU = @enumToInt(enum_unnamed_16.AKEYCODE_TV_MEDIA_CONTEXT_MENU); +pub const AKEYCODE_TV_TIMER_PROGRAMMING = @enumToInt(enum_unnamed_16.AKEYCODE_TV_TIMER_PROGRAMMING); +pub const AKEYCODE_HELP = @enumToInt(enum_unnamed_16.AKEYCODE_HELP); +pub const AKEYCODE_NAVIGATE_PREVIOUS = @enumToInt(enum_unnamed_16.AKEYCODE_NAVIGATE_PREVIOUS); +pub const AKEYCODE_NAVIGATE_NEXT = @enumToInt(enum_unnamed_16.AKEYCODE_NAVIGATE_NEXT); +pub const AKEYCODE_NAVIGATE_IN = @enumToInt(enum_unnamed_16.AKEYCODE_NAVIGATE_IN); +pub const AKEYCODE_NAVIGATE_OUT = @enumToInt(enum_unnamed_16.AKEYCODE_NAVIGATE_OUT); +pub const AKEYCODE_STEM_PRIMARY = @enumToInt(enum_unnamed_16.AKEYCODE_STEM_PRIMARY); +pub const AKEYCODE_STEM_1 = @enumToInt(enum_unnamed_16.AKEYCODE_STEM_1); +pub const AKEYCODE_STEM_2 = @enumToInt(enum_unnamed_16.AKEYCODE_STEM_2); +pub const AKEYCODE_STEM_3 = @enumToInt(enum_unnamed_16.AKEYCODE_STEM_3); +pub const AKEYCODE_DPAD_UP_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_UP_LEFT); +pub const AKEYCODE_DPAD_DOWN_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_DOWN_LEFT); +pub const AKEYCODE_DPAD_UP_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_UP_RIGHT); +pub const AKEYCODE_DPAD_DOWN_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_DPAD_DOWN_RIGHT); +pub const AKEYCODE_MEDIA_SKIP_FORWARD = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_SKIP_FORWARD); +pub const AKEYCODE_MEDIA_SKIP_BACKWARD = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_SKIP_BACKWARD); +pub const AKEYCODE_MEDIA_STEP_FORWARD = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_STEP_FORWARD); +pub const AKEYCODE_MEDIA_STEP_BACKWARD = @enumToInt(enum_unnamed_16.AKEYCODE_MEDIA_STEP_BACKWARD); +pub const AKEYCODE_SOFT_SLEEP = @enumToInt(enum_unnamed_16.AKEYCODE_SOFT_SLEEP); +pub const AKEYCODE_CUT = @enumToInt(enum_unnamed_16.AKEYCODE_CUT); +pub const AKEYCODE_COPY = @enumToInt(enum_unnamed_16.AKEYCODE_COPY); +pub const AKEYCODE_PASTE = @enumToInt(enum_unnamed_16.AKEYCODE_PASTE); +pub const AKEYCODE_SYSTEM_NAVIGATION_UP = @enumToInt(enum_unnamed_16.AKEYCODE_SYSTEM_NAVIGATION_UP); +pub const AKEYCODE_SYSTEM_NAVIGATION_DOWN = @enumToInt(enum_unnamed_16.AKEYCODE_SYSTEM_NAVIGATION_DOWN); +pub const AKEYCODE_SYSTEM_NAVIGATION_LEFT = @enumToInt(enum_unnamed_16.AKEYCODE_SYSTEM_NAVIGATION_LEFT); +pub const AKEYCODE_SYSTEM_NAVIGATION_RIGHT = @enumToInt(enum_unnamed_16.AKEYCODE_SYSTEM_NAVIGATION_RIGHT); +pub const AKEYCODE_ALL_APPS = @enumToInt(enum_unnamed_16.AKEYCODE_ALL_APPS); +pub const AKEYCODE_REFRESH = @enumToInt(enum_unnamed_16.AKEYCODE_REFRESH); +pub const AKEYCODE_THUMBS_UP = @enumToInt(enum_unnamed_16.AKEYCODE_THUMBS_UP); +pub const AKEYCODE_THUMBS_DOWN = @enumToInt(enum_unnamed_16.AKEYCODE_THUMBS_DOWN); +pub const AKEYCODE_PROFILE_SWITCH = @enumToInt(enum_unnamed_16.AKEYCODE_PROFILE_SWITCH); +const enum_unnamed_16 = enum(c_int) { + AKEYCODE_UNKNOWN = 0, + AKEYCODE_SOFT_LEFT = 1, + AKEYCODE_SOFT_RIGHT = 2, + AKEYCODE_HOME = 3, + AKEYCODE_BACK = 4, + AKEYCODE_CALL = 5, + AKEYCODE_ENDCALL = 6, + AKEYCODE_0 = 7, + AKEYCODE_1 = 8, + AKEYCODE_2 = 9, + AKEYCODE_3 = 10, + AKEYCODE_4 = 11, + AKEYCODE_5 = 12, + AKEYCODE_6 = 13, + AKEYCODE_7 = 14, + AKEYCODE_8 = 15, + AKEYCODE_9 = 16, + AKEYCODE_STAR = 17, + AKEYCODE_POUND = 18, + AKEYCODE_DPAD_UP = 19, + AKEYCODE_DPAD_DOWN = 20, + AKEYCODE_DPAD_LEFT = 21, + AKEYCODE_DPAD_RIGHT = 22, + AKEYCODE_DPAD_CENTER = 23, + AKEYCODE_VOLUME_UP = 24, + AKEYCODE_VOLUME_DOWN = 25, + AKEYCODE_POWER = 26, + AKEYCODE_CAMERA = 27, + AKEYCODE_CLEAR = 28, + AKEYCODE_A = 29, + AKEYCODE_B = 30, + AKEYCODE_C = 31, + AKEYCODE_D = 32, + AKEYCODE_E = 33, + AKEYCODE_F = 34, + AKEYCODE_G = 35, + AKEYCODE_H = 36, + AKEYCODE_I = 37, + AKEYCODE_J = 38, + AKEYCODE_K = 39, + AKEYCODE_L = 40, + AKEYCODE_M = 41, + AKEYCODE_N = 42, + AKEYCODE_O = 43, + AKEYCODE_P = 44, + AKEYCODE_Q = 45, + AKEYCODE_R = 46, + AKEYCODE_S = 47, + AKEYCODE_T = 48, + AKEYCODE_U = 49, + AKEYCODE_V = 50, + AKEYCODE_W = 51, + AKEYCODE_X = 52, + AKEYCODE_Y = 53, + AKEYCODE_Z = 54, + AKEYCODE_COMMA = 55, + AKEYCODE_PERIOD = 56, + AKEYCODE_ALT_LEFT = 57, + AKEYCODE_ALT_RIGHT = 58, + AKEYCODE_SHIFT_LEFT = 59, + AKEYCODE_SHIFT_RIGHT = 60, + AKEYCODE_TAB = 61, + AKEYCODE_SPACE = 62, + AKEYCODE_SYM = 63, + AKEYCODE_EXPLORER = 64, + AKEYCODE_ENVELOPE = 65, + AKEYCODE_ENTER = 66, + AKEYCODE_DEL = 67, + AKEYCODE_GRAVE = 68, + AKEYCODE_MINUS = 69, + AKEYCODE_EQUALS = 70, + AKEYCODE_LEFT_BRACKET = 71, + AKEYCODE_RIGHT_BRACKET = 72, + AKEYCODE_BACKSLASH = 73, + AKEYCODE_SEMICOLON = 74, + AKEYCODE_APOSTROPHE = 75, + AKEYCODE_SLASH = 76, + AKEYCODE_AT = 77, + AKEYCODE_NUM = 78, + AKEYCODE_HEADSETHOOK = 79, + AKEYCODE_FOCUS = 80, + AKEYCODE_PLUS = 81, + AKEYCODE_MENU = 82, + AKEYCODE_NOTIFICATION = 83, + AKEYCODE_SEARCH = 84, + AKEYCODE_MEDIA_PLAY_PAUSE = 85, + AKEYCODE_MEDIA_STOP = 86, + AKEYCODE_MEDIA_NEXT = 87, + AKEYCODE_MEDIA_PREVIOUS = 88, + AKEYCODE_MEDIA_REWIND = 89, + AKEYCODE_MEDIA_FAST_FORWARD = 90, + AKEYCODE_MUTE = 91, + AKEYCODE_PAGE_UP = 92, + AKEYCODE_PAGE_DOWN = 93, + AKEYCODE_PICTSYMBOLS = 94, + AKEYCODE_SWITCH_CHARSET = 95, + AKEYCODE_BUTTON_A = 96, + AKEYCODE_BUTTON_B = 97, + AKEYCODE_BUTTON_C = 98, + AKEYCODE_BUTTON_X = 99, + AKEYCODE_BUTTON_Y = 100, + AKEYCODE_BUTTON_Z = 101, + AKEYCODE_BUTTON_L1 = 102, + AKEYCODE_BUTTON_R1 = 103, + AKEYCODE_BUTTON_L2 = 104, + AKEYCODE_BUTTON_R2 = 105, + AKEYCODE_BUTTON_THUMBL = 106, + AKEYCODE_BUTTON_THUMBR = 107, + AKEYCODE_BUTTON_START = 108, + AKEYCODE_BUTTON_SELECT = 109, + AKEYCODE_BUTTON_MODE = 110, + AKEYCODE_ESCAPE = 111, + AKEYCODE_FORWARD_DEL = 112, + AKEYCODE_CTRL_LEFT = 113, + AKEYCODE_CTRL_RIGHT = 114, + AKEYCODE_CAPS_LOCK = 115, + AKEYCODE_SCROLL_LOCK = 116, + AKEYCODE_META_LEFT = 117, + AKEYCODE_META_RIGHT = 118, + AKEYCODE_FUNCTION = 119, + AKEYCODE_SYSRQ = 120, + AKEYCODE_BREAK = 121, + AKEYCODE_MOVE_HOME = 122, + AKEYCODE_MOVE_END = 123, + AKEYCODE_INSERT = 124, + AKEYCODE_FORWARD = 125, + AKEYCODE_MEDIA_PLAY = 126, + AKEYCODE_MEDIA_PAUSE = 127, + AKEYCODE_MEDIA_CLOSE = 128, + AKEYCODE_MEDIA_EJECT = 129, + AKEYCODE_MEDIA_RECORD = 130, + AKEYCODE_F1 = 131, + AKEYCODE_F2 = 132, + AKEYCODE_F3 = 133, + AKEYCODE_F4 = 134, + AKEYCODE_F5 = 135, + AKEYCODE_F6 = 136, + AKEYCODE_F7 = 137, + AKEYCODE_F8 = 138, + AKEYCODE_F9 = 139, + AKEYCODE_F10 = 140, + AKEYCODE_F11 = 141, + AKEYCODE_F12 = 142, + AKEYCODE_NUM_LOCK = 143, + AKEYCODE_NUMPAD_0 = 144, + AKEYCODE_NUMPAD_1 = 145, + AKEYCODE_NUMPAD_2 = 146, + AKEYCODE_NUMPAD_3 = 147, + AKEYCODE_NUMPAD_4 = 148, + AKEYCODE_NUMPAD_5 = 149, + AKEYCODE_NUMPAD_6 = 150, + AKEYCODE_NUMPAD_7 = 151, + AKEYCODE_NUMPAD_8 = 152, + AKEYCODE_NUMPAD_9 = 153, + AKEYCODE_NUMPAD_DIVIDE = 154, + AKEYCODE_NUMPAD_MULTIPLY = 155, + AKEYCODE_NUMPAD_SUBTRACT = 156, + AKEYCODE_NUMPAD_ADD = 157, + AKEYCODE_NUMPAD_DOT = 158, + AKEYCODE_NUMPAD_COMMA = 159, + AKEYCODE_NUMPAD_ENTER = 160, + AKEYCODE_NUMPAD_EQUALS = 161, + AKEYCODE_NUMPAD_LEFT_PAREN = 162, + AKEYCODE_NUMPAD_RIGHT_PAREN = 163, + AKEYCODE_VOLUME_MUTE = 164, + AKEYCODE_INFO = 165, + AKEYCODE_CHANNEL_UP = 166, + AKEYCODE_CHANNEL_DOWN = 167, + AKEYCODE_ZOOM_IN = 168, + AKEYCODE_ZOOM_OUT = 169, + AKEYCODE_TV = 170, + AKEYCODE_WINDOW = 171, + AKEYCODE_GUIDE = 172, + AKEYCODE_DVR = 173, + AKEYCODE_BOOKMARK = 174, + AKEYCODE_CAPTIONS = 175, + AKEYCODE_SETTINGS = 176, + AKEYCODE_TV_POWER = 177, + AKEYCODE_TV_INPUT = 178, + AKEYCODE_STB_POWER = 179, + AKEYCODE_STB_INPUT = 180, + AKEYCODE_AVR_POWER = 181, + AKEYCODE_AVR_INPUT = 182, + AKEYCODE_PROG_RED = 183, + AKEYCODE_PROG_GREEN = 184, + AKEYCODE_PROG_YELLOW = 185, + AKEYCODE_PROG_BLUE = 186, + AKEYCODE_APP_SWITCH = 187, + AKEYCODE_BUTTON_1 = 188, + AKEYCODE_BUTTON_2 = 189, + AKEYCODE_BUTTON_3 = 190, + AKEYCODE_BUTTON_4 = 191, + AKEYCODE_BUTTON_5 = 192, + AKEYCODE_BUTTON_6 = 193, + AKEYCODE_BUTTON_7 = 194, + AKEYCODE_BUTTON_8 = 195, + AKEYCODE_BUTTON_9 = 196, + AKEYCODE_BUTTON_10 = 197, + AKEYCODE_BUTTON_11 = 198, + AKEYCODE_BUTTON_12 = 199, + AKEYCODE_BUTTON_13 = 200, + AKEYCODE_BUTTON_14 = 201, + AKEYCODE_BUTTON_15 = 202, + AKEYCODE_BUTTON_16 = 203, + AKEYCODE_LANGUAGE_SWITCH = 204, + AKEYCODE_MANNER_MODE = 205, + AKEYCODE_3D_MODE = 206, + AKEYCODE_CONTACTS = 207, + AKEYCODE_CALENDAR = 208, + AKEYCODE_MUSIC = 209, + AKEYCODE_CALCULATOR = 210, + AKEYCODE_ZENKAKU_HANKAKU = 211, + AKEYCODE_EISU = 212, + AKEYCODE_MUHENKAN = 213, + AKEYCODE_HENKAN = 214, + AKEYCODE_KATAKANA_HIRAGANA = 215, + AKEYCODE_YEN = 216, + AKEYCODE_RO = 217, + AKEYCODE_KANA = 218, + AKEYCODE_ASSIST = 219, + AKEYCODE_BRIGHTNESS_DOWN = 220, + AKEYCODE_BRIGHTNESS_UP = 221, + AKEYCODE_MEDIA_AUDIO_TRACK = 222, + AKEYCODE_SLEEP = 223, + AKEYCODE_WAKEUP = 224, + AKEYCODE_PAIRING = 225, + AKEYCODE_MEDIA_TOP_MENU = 226, + AKEYCODE_11 = 227, + AKEYCODE_12 = 228, + AKEYCODE_LAST_CHANNEL = 229, + AKEYCODE_TV_DATA_SERVICE = 230, + AKEYCODE_VOICE_ASSIST = 231, + AKEYCODE_TV_RADIO_SERVICE = 232, + AKEYCODE_TV_TELETEXT = 233, + AKEYCODE_TV_NUMBER_ENTRY = 234, + AKEYCODE_TV_TERRESTRIAL_ANALOG = 235, + AKEYCODE_TV_TERRESTRIAL_DIGITAL = 236, + AKEYCODE_TV_SATELLITE = 237, + AKEYCODE_TV_SATELLITE_BS = 238, + AKEYCODE_TV_SATELLITE_CS = 239, + AKEYCODE_TV_SATELLITE_SERVICE = 240, + AKEYCODE_TV_NETWORK = 241, + AKEYCODE_TV_ANTENNA_CABLE = 242, + AKEYCODE_TV_INPUT_HDMI_1 = 243, + AKEYCODE_TV_INPUT_HDMI_2 = 244, + AKEYCODE_TV_INPUT_HDMI_3 = 245, + AKEYCODE_TV_INPUT_HDMI_4 = 246, + AKEYCODE_TV_INPUT_COMPOSITE_1 = 247, + AKEYCODE_TV_INPUT_COMPOSITE_2 = 248, + AKEYCODE_TV_INPUT_COMPONENT_1 = 249, + AKEYCODE_TV_INPUT_COMPONENT_2 = 250, + AKEYCODE_TV_INPUT_VGA_1 = 251, + AKEYCODE_TV_AUDIO_DESCRIPTION = 252, + AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP = 253, + AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN = 254, + AKEYCODE_TV_ZOOM_MODE = 255, + AKEYCODE_TV_CONTENTS_MENU = 256, + AKEYCODE_TV_MEDIA_CONTEXT_MENU = 257, + AKEYCODE_TV_TIMER_PROGRAMMING = 258, + AKEYCODE_HELP = 259, + AKEYCODE_NAVIGATE_PREVIOUS = 260, + AKEYCODE_NAVIGATE_NEXT = 261, + AKEYCODE_NAVIGATE_IN = 262, + AKEYCODE_NAVIGATE_OUT = 263, + AKEYCODE_STEM_PRIMARY = 264, + AKEYCODE_STEM_1 = 265, + AKEYCODE_STEM_2 = 266, + AKEYCODE_STEM_3 = 267, + AKEYCODE_DPAD_UP_LEFT = 268, + AKEYCODE_DPAD_DOWN_LEFT = 269, + AKEYCODE_DPAD_UP_RIGHT = 270, + AKEYCODE_DPAD_DOWN_RIGHT = 271, + AKEYCODE_MEDIA_SKIP_FORWARD = 272, + AKEYCODE_MEDIA_SKIP_BACKWARD = 273, + AKEYCODE_MEDIA_STEP_FORWARD = 274, + AKEYCODE_MEDIA_STEP_BACKWARD = 275, + AKEYCODE_SOFT_SLEEP = 276, + AKEYCODE_CUT = 277, + AKEYCODE_COPY = 278, + AKEYCODE_PASTE = 279, + AKEYCODE_SYSTEM_NAVIGATION_UP = 280, + AKEYCODE_SYSTEM_NAVIGATION_DOWN = 281, + AKEYCODE_SYSTEM_NAVIGATION_LEFT = 282, + AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283, + AKEYCODE_ALL_APPS = 284, + AKEYCODE_REFRESH = 285, + AKEYCODE_THUMBS_UP = 286, + AKEYCODE_THUMBS_DOWN = 287, + AKEYCODE_PROFILE_SWITCH = 288, + _, +}; +pub const AKEY_STATE_UNKNOWN = @enumToInt(enum_unnamed_17.AKEY_STATE_UNKNOWN); +pub const AKEY_STATE_UP = @enumToInt(enum_unnamed_17.AKEY_STATE_UP); +pub const AKEY_STATE_DOWN = @enumToInt(enum_unnamed_17.AKEY_STATE_DOWN); +pub const AKEY_STATE_VIRTUAL = @enumToInt(enum_unnamed_17.AKEY_STATE_VIRTUAL); +const enum_unnamed_17 = enum(c_int) { + AKEY_STATE_UNKNOWN = -1, + AKEY_STATE_UP = 0, + AKEY_STATE_DOWN = 1, + AKEY_STATE_VIRTUAL = 2, + _, +}; +pub const AMETA_NONE = @enumToInt(enum_unnamed_18.AMETA_NONE); +pub const AMETA_ALT_ON = @enumToInt(enum_unnamed_18.AMETA_ALT_ON); +pub const AMETA_ALT_LEFT_ON = @enumToInt(enum_unnamed_18.AMETA_ALT_LEFT_ON); +pub const AMETA_ALT_RIGHT_ON = @enumToInt(enum_unnamed_18.AMETA_ALT_RIGHT_ON); +pub const AMETA_SHIFT_ON = @enumToInt(enum_unnamed_18.AMETA_SHIFT_ON); +pub const AMETA_SHIFT_LEFT_ON = @enumToInt(enum_unnamed_18.AMETA_SHIFT_LEFT_ON); +pub const AMETA_SHIFT_RIGHT_ON = @enumToInt(enum_unnamed_18.AMETA_SHIFT_RIGHT_ON); +pub const AMETA_SYM_ON = @enumToInt(enum_unnamed_18.AMETA_SYM_ON); +pub const AMETA_FUNCTION_ON = @enumToInt(enum_unnamed_18.AMETA_FUNCTION_ON); +pub const AMETA_CTRL_ON = @enumToInt(enum_unnamed_18.AMETA_CTRL_ON); +pub const AMETA_CTRL_LEFT_ON = @enumToInt(enum_unnamed_18.AMETA_CTRL_LEFT_ON); +pub const AMETA_CTRL_RIGHT_ON = @enumToInt(enum_unnamed_18.AMETA_CTRL_RIGHT_ON); +pub const AMETA_META_ON = @enumToInt(enum_unnamed_18.AMETA_META_ON); +pub const AMETA_META_LEFT_ON = @enumToInt(enum_unnamed_18.AMETA_META_LEFT_ON); +pub const AMETA_META_RIGHT_ON = @enumToInt(enum_unnamed_18.AMETA_META_RIGHT_ON); +pub const AMETA_CAPS_LOCK_ON = @enumToInt(enum_unnamed_18.AMETA_CAPS_LOCK_ON); +pub const AMETA_NUM_LOCK_ON = @enumToInt(enum_unnamed_18.AMETA_NUM_LOCK_ON); +pub const AMETA_SCROLL_LOCK_ON = @enumToInt(enum_unnamed_18.AMETA_SCROLL_LOCK_ON); +const enum_unnamed_18 = enum(c_int) { + AMETA_NONE = 0, + AMETA_ALT_ON = 2, + AMETA_ALT_LEFT_ON = 16, + AMETA_ALT_RIGHT_ON = 32, + AMETA_SHIFT_ON = 1, + AMETA_SHIFT_LEFT_ON = 64, + AMETA_SHIFT_RIGHT_ON = 128, + AMETA_SYM_ON = 4, + AMETA_FUNCTION_ON = 8, + AMETA_CTRL_ON = 4096, + AMETA_CTRL_LEFT_ON = 8192, + AMETA_CTRL_RIGHT_ON = 16384, + AMETA_META_ON = 65536, + AMETA_META_LEFT_ON = 131072, + AMETA_META_RIGHT_ON = 262144, + AMETA_CAPS_LOCK_ON = 1048576, + AMETA_NUM_LOCK_ON = 2097152, + AMETA_SCROLL_LOCK_ON = 4194304, + _, +}; +pub const AInputEvent = opaque {}; +pub const AInputEventType = enum(c_int) { + AINPUT_EVENT_TYPE_KEY = 1, + AINPUT_EVENT_TYPE_MOTION = 2, + _, +}; +pub const AKEY_EVENT_ACTION_DOWN = @enumToInt(AKeyEventActionType.AKEY_EVENT_ACTION_DOWN); +pub const AKEY_EVENT_ACTION_UP = @enumToInt(AKeyEventActionType.AKEY_EVENT_ACTION_UP); +pub const AKEY_EVENT_ACTION_MULTIPLE = @enumToInt(AKeyEventActionType.AKEY_EVENT_ACTION_MULTIPLE); +pub const AKeyEventActionType = enum(c_int) { + AKEY_EVENT_ACTION_DOWN = 0, + AKEY_EVENT_ACTION_UP = 1, + AKEY_EVENT_ACTION_MULTIPLE = 2, + _, +}; +pub const AKEY_EVENT_FLAG_WOKE_HERE = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_WOKE_HERE); +pub const AKEY_EVENT_FLAG_SOFT_KEYBOARD = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_SOFT_KEYBOARD); +pub const AKEY_EVENT_FLAG_KEEP_TOUCH_MODE = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_KEEP_TOUCH_MODE); +pub const AKEY_EVENT_FLAG_FROM_SYSTEM = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_FROM_SYSTEM); +pub const AKEY_EVENT_FLAG_EDITOR_ACTION = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_EDITOR_ACTION); +pub const AKEY_EVENT_FLAG_CANCELED = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_CANCELED); +pub const AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); +pub const AKEY_EVENT_FLAG_LONG_PRESS = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_LONG_PRESS); +pub const AKEY_EVENT_FLAG_CANCELED_LONG_PRESS = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_CANCELED_LONG_PRESS); +pub const AKEY_EVENT_FLAG_TRACKING = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_TRACKING); +pub const AKEY_EVENT_FLAG_FALLBACK = @enumToInt(enum_unnamed_21.AKEY_EVENT_FLAG_FALLBACK); +const enum_unnamed_21 = enum(c_int) { + AKEY_EVENT_FLAG_WOKE_HERE = 1, + AKEY_EVENT_FLAG_SOFT_KEYBOARD = 2, + AKEY_EVENT_FLAG_KEEP_TOUCH_MODE = 4, + AKEY_EVENT_FLAG_FROM_SYSTEM = 8, + AKEY_EVENT_FLAG_EDITOR_ACTION = 16, + AKEY_EVENT_FLAG_CANCELED = 32, + AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY = 64, + AKEY_EVENT_FLAG_LONG_PRESS = 128, + AKEY_EVENT_FLAG_CANCELED_LONG_PRESS = 256, + AKEY_EVENT_FLAG_TRACKING = 512, + AKEY_EVENT_FLAG_FALLBACK = 1024, + _, +}; +pub const AMOTION_EVENT_ACTION_MASK = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_MASK); +pub const AMOTION_EVENT_ACTION_POINTER_INDEX_MASK = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_POINTER_INDEX_MASK); +pub const AMOTION_EVENT_ACTION_DOWN = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_DOWN); +pub const AMOTION_EVENT_ACTION_UP = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_UP); +pub const AMOTION_EVENT_ACTION_MOVE = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_MOVE); +pub const AMOTION_EVENT_ACTION_CANCEL = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_CANCEL); +pub const AMOTION_EVENT_ACTION_OUTSIDE = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_OUTSIDE); +pub const AMOTION_EVENT_ACTION_POINTER_DOWN = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_POINTER_DOWN); +pub const AMOTION_EVENT_ACTION_POINTER_UP = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_POINTER_UP); +pub const AMOTION_EVENT_ACTION_HOVER_MOVE = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_HOVER_MOVE); +pub const AMOTION_EVENT_ACTION_SCROLL = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_SCROLL); +pub const AMOTION_EVENT_ACTION_HOVER_ENTER = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_HOVER_ENTER); +pub const AMOTION_EVENT_ACTION_HOVER_EXIT = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_HOVER_EXIT); +pub const AMOTION_EVENT_ACTION_BUTTON_PRESS = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_BUTTON_PRESS); +pub const AMOTION_EVENT_ACTION_BUTTON_RELEASE = @enumToInt(AMotionEventActionType.AMOTION_EVENT_ACTION_BUTTON_RELEASE); +pub const AMotionEventActionType = enum(c_int) { + AMOTION_EVENT_ACTION_MASK = 255, + AMOTION_EVENT_ACTION_POINTER_INDEX_MASK = 65280, + AMOTION_EVENT_ACTION_DOWN = 0, + AMOTION_EVENT_ACTION_UP = 1, + AMOTION_EVENT_ACTION_MOVE = 2, + AMOTION_EVENT_ACTION_CANCEL = 3, + AMOTION_EVENT_ACTION_OUTSIDE = 4, + AMOTION_EVENT_ACTION_POINTER_DOWN = 5, + AMOTION_EVENT_ACTION_POINTER_UP = 6, + AMOTION_EVENT_ACTION_HOVER_MOVE = 7, + AMOTION_EVENT_ACTION_SCROLL = 8, + AMOTION_EVENT_ACTION_HOVER_ENTER = 9, + AMOTION_EVENT_ACTION_HOVER_EXIT = 10, + AMOTION_EVENT_ACTION_BUTTON_PRESS = 11, + AMOTION_EVENT_ACTION_BUTTON_RELEASE = 12, + _, +}; +pub const AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = @enumToInt(enum_unnamed_23.AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED); +const enum_unnamed_23 = enum(c_int) { + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 1, + _, +}; +pub const AMOTION_EVENT_EDGE_FLAG_NONE = @enumToInt(enum_unnamed_24.AMOTION_EVENT_EDGE_FLAG_NONE); +pub const AMOTION_EVENT_EDGE_FLAG_TOP = @enumToInt(enum_unnamed_24.AMOTION_EVENT_EDGE_FLAG_TOP); +pub const AMOTION_EVENT_EDGE_FLAG_BOTTOM = @enumToInt(enum_unnamed_24.AMOTION_EVENT_EDGE_FLAG_BOTTOM); +pub const AMOTION_EVENT_EDGE_FLAG_LEFT = @enumToInt(enum_unnamed_24.AMOTION_EVENT_EDGE_FLAG_LEFT); +pub const AMOTION_EVENT_EDGE_FLAG_RIGHT = @enumToInt(enum_unnamed_24.AMOTION_EVENT_EDGE_FLAG_RIGHT); +const enum_unnamed_24 = enum(c_int) { + AMOTION_EVENT_EDGE_FLAG_NONE = 0, + AMOTION_EVENT_EDGE_FLAG_TOP = 1, + AMOTION_EVENT_EDGE_FLAG_BOTTOM = 2, + AMOTION_EVENT_EDGE_FLAG_LEFT = 4, + AMOTION_EVENT_EDGE_FLAG_RIGHT = 8, + _, +}; +pub const AMOTION_EVENT_AXIS_X = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_X); +pub const AMOTION_EVENT_AXIS_Y = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_Y); +pub const AMOTION_EVENT_AXIS_PRESSURE = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_PRESSURE); +pub const AMOTION_EVENT_AXIS_SIZE = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_SIZE); +pub const AMOTION_EVENT_AXIS_TOUCH_MAJOR = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_TOUCH_MAJOR); +pub const AMOTION_EVENT_AXIS_TOUCH_MINOR = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_TOUCH_MINOR); +pub const AMOTION_EVENT_AXIS_TOOL_MAJOR = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_TOOL_MAJOR); +pub const AMOTION_EVENT_AXIS_TOOL_MINOR = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_TOOL_MINOR); +pub const AMOTION_EVENT_AXIS_ORIENTATION = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_ORIENTATION); +pub const AMOTION_EVENT_AXIS_VSCROLL = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_VSCROLL); +pub const AMOTION_EVENT_AXIS_HSCROLL = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_HSCROLL); +pub const AMOTION_EVENT_AXIS_Z = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_Z); +pub const AMOTION_EVENT_AXIS_RX = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_RX); +pub const AMOTION_EVENT_AXIS_RY = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_RY); +pub const AMOTION_EVENT_AXIS_RZ = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_RZ); +pub const AMOTION_EVENT_AXIS_HAT_X = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_HAT_X); +pub const AMOTION_EVENT_AXIS_HAT_Y = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_HAT_Y); +pub const AMOTION_EVENT_AXIS_LTRIGGER = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_LTRIGGER); +pub const AMOTION_EVENT_AXIS_RTRIGGER = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_RTRIGGER); +pub const AMOTION_EVENT_AXIS_THROTTLE = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_THROTTLE); +pub const AMOTION_EVENT_AXIS_RUDDER = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_RUDDER); +pub const AMOTION_EVENT_AXIS_WHEEL = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_WHEEL); +pub const AMOTION_EVENT_AXIS_GAS = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GAS); +pub const AMOTION_EVENT_AXIS_BRAKE = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_BRAKE); +pub const AMOTION_EVENT_AXIS_DISTANCE = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_DISTANCE); +pub const AMOTION_EVENT_AXIS_TILT = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_TILT); +pub const AMOTION_EVENT_AXIS_SCROLL = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_SCROLL); +pub const AMOTION_EVENT_AXIS_RELATIVE_X = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_RELATIVE_X); +pub const AMOTION_EVENT_AXIS_RELATIVE_Y = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_RELATIVE_Y); +pub const AMOTION_EVENT_AXIS_GENERIC_1 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_1); +pub const AMOTION_EVENT_AXIS_GENERIC_2 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_2); +pub const AMOTION_EVENT_AXIS_GENERIC_3 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_3); +pub const AMOTION_EVENT_AXIS_GENERIC_4 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_4); +pub const AMOTION_EVENT_AXIS_GENERIC_5 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_5); +pub const AMOTION_EVENT_AXIS_GENERIC_6 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_6); +pub const AMOTION_EVENT_AXIS_GENERIC_7 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_7); +pub const AMOTION_EVENT_AXIS_GENERIC_8 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_8); +pub const AMOTION_EVENT_AXIS_GENERIC_9 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_9); +pub const AMOTION_EVENT_AXIS_GENERIC_10 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_10); +pub const AMOTION_EVENT_AXIS_GENERIC_11 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_11); +pub const AMOTION_EVENT_AXIS_GENERIC_12 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_12); +pub const AMOTION_EVENT_AXIS_GENERIC_13 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_13); +pub const AMOTION_EVENT_AXIS_GENERIC_14 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_14); +pub const AMOTION_EVENT_AXIS_GENERIC_15 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_15); +pub const AMOTION_EVENT_AXIS_GENERIC_16 = @enumToInt(enum_unnamed_25.AMOTION_EVENT_AXIS_GENERIC_16); +const enum_unnamed_25 = enum(c_int) { + AMOTION_EVENT_AXIS_X = 0, + AMOTION_EVENT_AXIS_Y = 1, + AMOTION_EVENT_AXIS_PRESSURE = 2, + AMOTION_EVENT_AXIS_SIZE = 3, + AMOTION_EVENT_AXIS_TOUCH_MAJOR = 4, + AMOTION_EVENT_AXIS_TOUCH_MINOR = 5, + AMOTION_EVENT_AXIS_TOOL_MAJOR = 6, + AMOTION_EVENT_AXIS_TOOL_MINOR = 7, + AMOTION_EVENT_AXIS_ORIENTATION = 8, + AMOTION_EVENT_AXIS_VSCROLL = 9, + AMOTION_EVENT_AXIS_HSCROLL = 10, + AMOTION_EVENT_AXIS_Z = 11, + AMOTION_EVENT_AXIS_RX = 12, + AMOTION_EVENT_AXIS_RY = 13, + AMOTION_EVENT_AXIS_RZ = 14, + AMOTION_EVENT_AXIS_HAT_X = 15, + AMOTION_EVENT_AXIS_HAT_Y = 16, + AMOTION_EVENT_AXIS_LTRIGGER = 17, + AMOTION_EVENT_AXIS_RTRIGGER = 18, + AMOTION_EVENT_AXIS_THROTTLE = 19, + AMOTION_EVENT_AXIS_RUDDER = 20, + AMOTION_EVENT_AXIS_WHEEL = 21, + AMOTION_EVENT_AXIS_GAS = 22, + AMOTION_EVENT_AXIS_BRAKE = 23, + AMOTION_EVENT_AXIS_DISTANCE = 24, + AMOTION_EVENT_AXIS_TILT = 25, + AMOTION_EVENT_AXIS_SCROLL = 26, + AMOTION_EVENT_AXIS_RELATIVE_X = 27, + AMOTION_EVENT_AXIS_RELATIVE_Y = 28, + AMOTION_EVENT_AXIS_GENERIC_1 = 32, + AMOTION_EVENT_AXIS_GENERIC_2 = 33, + AMOTION_EVENT_AXIS_GENERIC_3 = 34, + AMOTION_EVENT_AXIS_GENERIC_4 = 35, + AMOTION_EVENT_AXIS_GENERIC_5 = 36, + AMOTION_EVENT_AXIS_GENERIC_6 = 37, + AMOTION_EVENT_AXIS_GENERIC_7 = 38, + AMOTION_EVENT_AXIS_GENERIC_8 = 39, + AMOTION_EVENT_AXIS_GENERIC_9 = 40, + AMOTION_EVENT_AXIS_GENERIC_10 = 41, + AMOTION_EVENT_AXIS_GENERIC_11 = 42, + AMOTION_EVENT_AXIS_GENERIC_12 = 43, + AMOTION_EVENT_AXIS_GENERIC_13 = 44, + AMOTION_EVENT_AXIS_GENERIC_14 = 45, + AMOTION_EVENT_AXIS_GENERIC_15 = 46, + AMOTION_EVENT_AXIS_GENERIC_16 = 47, + _, +}; +pub const AMOTION_EVENT_BUTTON_PRIMARY = @enumToInt(enum_unnamed_26.AMOTION_EVENT_BUTTON_PRIMARY); +pub const AMOTION_EVENT_BUTTON_SECONDARY = @enumToInt(enum_unnamed_26.AMOTION_EVENT_BUTTON_SECONDARY); +pub const AMOTION_EVENT_BUTTON_TERTIARY = @enumToInt(enum_unnamed_26.AMOTION_EVENT_BUTTON_TERTIARY); +pub const AMOTION_EVENT_BUTTON_BACK = @enumToInt(enum_unnamed_26.AMOTION_EVENT_BUTTON_BACK); +pub const AMOTION_EVENT_BUTTON_FORWARD = @enumToInt(enum_unnamed_26.AMOTION_EVENT_BUTTON_FORWARD); +pub const AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = @enumToInt(enum_unnamed_26.AMOTION_EVENT_BUTTON_STYLUS_PRIMARY); +pub const AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = @enumToInt(enum_unnamed_26.AMOTION_EVENT_BUTTON_STYLUS_SECONDARY); +const enum_unnamed_26 = enum(c_int) { + AMOTION_EVENT_BUTTON_PRIMARY = 1, + AMOTION_EVENT_BUTTON_SECONDARY = 2, + AMOTION_EVENT_BUTTON_TERTIARY = 4, + AMOTION_EVENT_BUTTON_BACK = 8, + AMOTION_EVENT_BUTTON_FORWARD = 16, + AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = 32, + AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = 64, + _, +}; +pub const AMOTION_EVENT_TOOL_TYPE_UNKNOWN = @enumToInt(enum_unnamed_27.AMOTION_EVENT_TOOL_TYPE_UNKNOWN); +pub const AMOTION_EVENT_TOOL_TYPE_FINGER = @enumToInt(enum_unnamed_27.AMOTION_EVENT_TOOL_TYPE_FINGER); +pub const AMOTION_EVENT_TOOL_TYPE_STYLUS = @enumToInt(enum_unnamed_27.AMOTION_EVENT_TOOL_TYPE_STYLUS); +pub const AMOTION_EVENT_TOOL_TYPE_MOUSE = @enumToInt(enum_unnamed_27.AMOTION_EVENT_TOOL_TYPE_MOUSE); +pub const AMOTION_EVENT_TOOL_TYPE_ERASER = @enumToInt(enum_unnamed_27.AMOTION_EVENT_TOOL_TYPE_ERASER); +const enum_unnamed_27 = enum(c_int) { + AMOTION_EVENT_TOOL_TYPE_UNKNOWN = 0, + AMOTION_EVENT_TOOL_TYPE_FINGER = 1, + AMOTION_EVENT_TOOL_TYPE_STYLUS = 2, + AMOTION_EVENT_TOOL_TYPE_MOUSE = 3, + AMOTION_EVENT_TOOL_TYPE_ERASER = 4, + _, +}; +pub const AINPUT_SOURCE_CLASS_MASK = @enumToInt(enum_unnamed_28.AINPUT_SOURCE_CLASS_MASK); +pub const AINPUT_SOURCE_CLASS_NONE = @enumToInt(enum_unnamed_28.AINPUT_SOURCE_CLASS_NONE); +pub const AINPUT_SOURCE_CLASS_BUTTON = @enumToInt(enum_unnamed_28.AINPUT_SOURCE_CLASS_BUTTON); +pub const AINPUT_SOURCE_CLASS_POINTER = @enumToInt(enum_unnamed_28.AINPUT_SOURCE_CLASS_POINTER); +pub const AINPUT_SOURCE_CLASS_NAVIGATION = @enumToInt(enum_unnamed_28.AINPUT_SOURCE_CLASS_NAVIGATION); +pub const AINPUT_SOURCE_CLASS_POSITION = @enumToInt(enum_unnamed_28.AINPUT_SOURCE_CLASS_POSITION); +pub const AINPUT_SOURCE_CLASS_JOYSTICK = @enumToInt(enum_unnamed_28.AINPUT_SOURCE_CLASS_JOYSTICK); +const enum_unnamed_28 = enum(c_int) { + AINPUT_SOURCE_CLASS_MASK = 255, + AINPUT_SOURCE_CLASS_NONE = 0, + AINPUT_SOURCE_CLASS_BUTTON = 1, + AINPUT_SOURCE_CLASS_POINTER = 2, + AINPUT_SOURCE_CLASS_NAVIGATION = 4, + AINPUT_SOURCE_CLASS_POSITION = 8, + AINPUT_SOURCE_CLASS_JOYSTICK = 16, + _, +}; +pub const AINPUT_SOURCE_UNKNOWN = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_UNKNOWN); +pub const AINPUT_SOURCE_KEYBOARD = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_KEYBOARD); +pub const AINPUT_SOURCE_DPAD = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_DPAD); +pub const AINPUT_SOURCE_GAMEPAD = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_GAMEPAD); +pub const AINPUT_SOURCE_TOUCHSCREEN = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_TOUCHSCREEN); +pub const AINPUT_SOURCE_MOUSE = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_MOUSE); +pub const AINPUT_SOURCE_STYLUS = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_STYLUS); +pub const AINPUT_SOURCE_BLUETOOTH_STYLUS = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_BLUETOOTH_STYLUS); +pub const AINPUT_SOURCE_TRACKBALL = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_TRACKBALL); +pub const AINPUT_SOURCE_MOUSE_RELATIVE = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_MOUSE_RELATIVE); +pub const AINPUT_SOURCE_TOUCHPAD = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_TOUCHPAD); +pub const AINPUT_SOURCE_TOUCH_NAVIGATION = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_TOUCH_NAVIGATION); +pub const AINPUT_SOURCE_JOYSTICK = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_JOYSTICK); +pub const AINPUT_SOURCE_ROTARY_ENCODER = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_ROTARY_ENCODER); +pub const AINPUT_SOURCE_ANY = @enumToInt(enum_unnamed_29.AINPUT_SOURCE_ANY); +const enum_unnamed_29 = enum(c_int) { + AINPUT_SOURCE_UNKNOWN = 0, + AINPUT_SOURCE_KEYBOARD = 257, + AINPUT_SOURCE_DPAD = 513, + AINPUT_SOURCE_GAMEPAD = 1025, + AINPUT_SOURCE_TOUCHSCREEN = 4098, + AINPUT_SOURCE_MOUSE = 8194, + AINPUT_SOURCE_STYLUS = 16386, + AINPUT_SOURCE_BLUETOOTH_STYLUS = 49154, + AINPUT_SOURCE_TRACKBALL = 65540, + AINPUT_SOURCE_MOUSE_RELATIVE = 131076, + AINPUT_SOURCE_TOUCHPAD = 1048584, + AINPUT_SOURCE_TOUCH_NAVIGATION = 2097152, + AINPUT_SOURCE_JOYSTICK = 16777232, + AINPUT_SOURCE_ROTARY_ENCODER = 4194304, + AINPUT_SOURCE_ANY = 4294967040, + _, +}; +pub const AINPUT_KEYBOARD_TYPE_NONE = @enumToInt(enum_unnamed_30.AINPUT_KEYBOARD_TYPE_NONE); +pub const AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC = @enumToInt(enum_unnamed_30.AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); +pub const AINPUT_KEYBOARD_TYPE_ALPHABETIC = @enumToInt(enum_unnamed_30.AINPUT_KEYBOARD_TYPE_ALPHABETIC); +const enum_unnamed_30 = enum(c_int) { + AINPUT_KEYBOARD_TYPE_NONE = 0, + AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC = 1, + AINPUT_KEYBOARD_TYPE_ALPHABETIC = 2, + _, +}; +pub const AINPUT_MOTION_RANGE_X = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_X); +pub const AINPUT_MOTION_RANGE_Y = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_Y); +pub const AINPUT_MOTION_RANGE_PRESSURE = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_PRESSURE); +pub const AINPUT_MOTION_RANGE_SIZE = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_SIZE); +pub const AINPUT_MOTION_RANGE_TOUCH_MAJOR = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_TOUCH_MAJOR); +pub const AINPUT_MOTION_RANGE_TOUCH_MINOR = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_TOUCH_MINOR); +pub const AINPUT_MOTION_RANGE_TOOL_MAJOR = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_TOOL_MAJOR); +pub const AINPUT_MOTION_RANGE_TOOL_MINOR = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_TOOL_MINOR); +pub const AINPUT_MOTION_RANGE_ORIENTATION = @enumToInt(enum_unnamed_31.AINPUT_MOTION_RANGE_ORIENTATION); +const enum_unnamed_31 = enum(c_int) { + AINPUT_MOTION_RANGE_X = 0, + AINPUT_MOTION_RANGE_Y = 1, + AINPUT_MOTION_RANGE_PRESSURE = 2, + AINPUT_MOTION_RANGE_SIZE = 3, + AINPUT_MOTION_RANGE_TOUCH_MAJOR = 4, + AINPUT_MOTION_RANGE_TOUCH_MINOR = 5, + AINPUT_MOTION_RANGE_TOOL_MAJOR = 6, + AINPUT_MOTION_RANGE_TOOL_MINOR = 7, + AINPUT_MOTION_RANGE_ORIENTATION = 8, + _, +}; +pub extern fn AInputEvent_getType(event: ?*const AInputEvent) i32; +pub extern fn AInputEvent_getDeviceId(event: ?*const AInputEvent) i32; +pub extern fn AInputEvent_getSource(event: ?*const AInputEvent) i32; +pub extern fn AKeyEvent_getAction(key_event: ?*const AInputEvent) i32; +pub extern fn AKeyEvent_getFlags(key_event: ?*const AInputEvent) i32; +pub extern fn AKeyEvent_getKeyCode(key_event: ?*const AInputEvent) i32; +pub extern fn AKeyEvent_getScanCode(key_event: ?*const AInputEvent) i32; +pub extern fn AKeyEvent_getMetaState(key_event: ?*const AInputEvent) i32; +pub extern fn AKeyEvent_getRepeatCount(key_event: ?*const AInputEvent) i32; +pub extern fn AKeyEvent_getDownTime(key_event: ?*const AInputEvent) c_long; +pub extern fn AKeyEvent_getEventTime(key_event: ?*const AInputEvent) c_long; +pub extern fn AMotionEvent_getAction(motion_event: ?*const AInputEvent) i32; +pub extern fn AMotionEvent_getFlags(motion_event: ?*const AInputEvent) i32; +pub extern fn AMotionEvent_getMetaState(motion_event: ?*const AInputEvent) i32; +pub extern fn AMotionEvent_getButtonState(motion_event: ?*const AInputEvent) i32; +pub extern fn AMotionEvent_getEdgeFlags(motion_event: ?*const AInputEvent) i32; +pub extern fn AMotionEvent_getDownTime(motion_event: ?*const AInputEvent) c_long; +pub extern fn AMotionEvent_getEventTime(motion_event: ?*const AInputEvent) c_long; +pub extern fn AMotionEvent_getXOffset(motion_event: ?*const AInputEvent) f32; +pub extern fn AMotionEvent_getYOffset(motion_event: ?*const AInputEvent) f32; +pub extern fn AMotionEvent_getXPrecision(motion_event: ?*const AInputEvent) f32; +pub extern fn AMotionEvent_getYPrecision(motion_event: ?*const AInputEvent) f32; +pub extern fn AMotionEvent_getPointerCount(motion_event: ?*const AInputEvent) usize; +pub extern fn AMotionEvent_getPointerId(motion_event: ?*const AInputEvent, pointer_index: usize) i32; +pub extern fn AMotionEvent_getToolType(motion_event: ?*const AInputEvent, pointer_index: usize) i32; +pub extern fn AMotionEvent_getRawX(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getRawY(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getX(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getY(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getPressure(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getSize(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getTouchMajor(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getTouchMinor(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getToolMajor(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getToolMinor(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getOrientation(motion_event: ?*const AInputEvent, pointer_index: usize) f32; +pub extern fn AMotionEvent_getAxisValue(motion_event: ?*const AInputEvent, axis: i32, pointer_index: usize) f32; +pub extern fn AMotionEvent_getHistorySize(motion_event: ?*const AInputEvent) usize; +pub extern fn AMotionEvent_getHistoricalEventTime(motion_event: ?*const AInputEvent, history_index: usize) i64; +pub extern fn AMotionEvent_getHistoricalRawX(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalRawY(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalX(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalY(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalPressure(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalSize(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalTouchMajor(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalTouchMinor(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalToolMajor(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalToolMinor(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalOrientation(motion_event: ?*const AInputEvent, pointer_index: usize, history_index: usize) f32; +pub extern fn AMotionEvent_getHistoricalAxisValue(motion_event: ?*const AInputEvent, axis: i32, pointer_index: usize, history_index: usize) f32; + +pub const AInputQueue = opaque {}; + +pub extern fn AInputQueue_attachLooper(queue: ?*AInputQueue, looper: ?*ALooper, ident: c_int, callback: ALooper_callbackFunc, data: ?*anyopaque) void; +pub extern fn AInputQueue_detachLooper(queue: ?*AInputQueue) void; +pub extern fn AInputQueue_hasEvents(queue: ?*AInputQueue) i32; +pub extern fn AInputQueue_getEvent(queue: ?*AInputQueue, outEvent: *?*AInputEvent) i32; +pub extern fn AInputQueue_preDispatchEvent(queue: ?*AInputQueue, event: ?*AInputEvent) i32; +pub extern fn AInputQueue_finishEvent(queue: ?*AInputQueue, event: ?*AInputEvent, handled: c_int) void; +const struct_unnamed_32 = extern struct { + quot: intmax_t, + rem: intmax_t, +}; +pub const imaxdiv_t = struct_unnamed_32; +pub extern fn imaxabs(__i: intmax_t) intmax_t; +pub extern fn imaxdiv(__numerator: intmax_t, __denominator: intmax_t) imaxdiv_t; +pub extern fn strtoimax(__s: [*c]const u8, __end_ptr: [*c][*c]u8, __base: c_int) intmax_t; +pub extern fn strtoumax(__s: [*c]const u8, __end_ptr: [*c][*c]u8, __base: c_int) uintmax_t; +pub extern fn wcstoimax(__s: [*c]const wchar_t, __end_ptr: [*c][*c]wchar_t, __base: c_int) intmax_t; +pub extern fn wcstoumax(__s: [*c]const wchar_t, __end_ptr: [*c][*c]wchar_t, __base: c_int) uintmax_t; +pub const ADATASPACE_UNKNOWN = @enumToInt(enum_ADataSpace.ADATASPACE_UNKNOWN); +pub const ADATASPACE_SCRGB_LINEAR = @enumToInt(enum_ADataSpace.ADATASPACE_SCRGB_LINEAR); +pub const ADATASPACE_SRGB = @enumToInt(enum_ADataSpace.ADATASPACE_SRGB); +pub const ADATASPACE_SCRGB = @enumToInt(enum_ADataSpace.ADATASPACE_SCRGB); +pub const ADATASPACE_DISPLAY_P3 = @enumToInt(enum_ADataSpace.ADATASPACE_DISPLAY_P3); +pub const ADATASPACE_BT2020_PQ = @enumToInt(enum_ADataSpace.ADATASPACE_BT2020_PQ); +pub const enum_ADataSpace = enum(c_int) { + ADATASPACE_UNKNOWN = 0, + ADATASPACE_SCRGB_LINEAR = 406913024, + ADATASPACE_SRGB = 142671872, + ADATASPACE_SCRGB = 411107328, + ADATASPACE_DISPLAY_P3 = 143261696, + ADATASPACE_BT2020_PQ = 163971072, + _, +}; +pub const struct_ARect = extern struct { + left: i32, + top: i32, + right: i32, + bottom: i32, +}; +pub const ARect = struct_ARect; +pub const AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM); +pub const AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM); +pub const AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM); +pub const AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM); +pub const AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT); +pub const AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM); +pub const AHARDWAREBUFFER_FORMAT_BLOB = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_BLOB); +pub const AHARDWAREBUFFER_FORMAT_D16_UNORM = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_D16_UNORM); +pub const AHARDWAREBUFFER_FORMAT_D24_UNORM = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_D24_UNORM); +pub const AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT); +pub const AHARDWAREBUFFER_FORMAT_D32_FLOAT = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_D32_FLOAT); +pub const AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT); +pub const AHARDWAREBUFFER_FORMAT_S8_UINT = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_S8_UINT); +pub const AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = @enumToInt(enum_AHardwareBuffer_Format.AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420); +pub const enum_AHardwareBuffer_Format = enum(c_int) { + AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1, + AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2, + AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3, + AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = 4, + AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT = 22, + AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 43, + AHARDWAREBUFFER_FORMAT_BLOB = 33, + AHARDWAREBUFFER_FORMAT_D16_UNORM = 48, + AHARDWAREBUFFER_FORMAT_D24_UNORM = 49, + AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT = 50, + AHARDWAREBUFFER_FORMAT_D32_FLOAT = 51, + AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT = 52, + AHARDWAREBUFFER_FORMAT_S8_UINT = 53, + AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 35, + _, +}; +pub const AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_CPU_READ_NEVER); +pub const AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_CPU_READ_RARELY); +pub const AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN); +pub const AHARDWAREBUFFER_USAGE_CPU_READ_MASK = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_CPU_READ_MASK); +pub const AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER); +pub const AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY); +pub const AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN); +pub const AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK); +pub const AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE); +pub const AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER); +pub const AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT); +pub const AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY); +pub const AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); +pub const AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VIDEO_ENCODE); +pub const AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA); +pub const AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER); +pub const AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP); +pub const AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE); +pub const AHARDWAREBUFFER_USAGE_VENDOR_0 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_0); +pub const AHARDWAREBUFFER_USAGE_VENDOR_1 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_1); +pub const AHARDWAREBUFFER_USAGE_VENDOR_2 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_2); +pub const AHARDWAREBUFFER_USAGE_VENDOR_3 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_3); +pub const AHARDWAREBUFFER_USAGE_VENDOR_4 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_4); +pub const AHARDWAREBUFFER_USAGE_VENDOR_5 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_5); +pub const AHARDWAREBUFFER_USAGE_VENDOR_6 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_6); +pub const AHARDWAREBUFFER_USAGE_VENDOR_7 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_7); +pub const AHARDWAREBUFFER_USAGE_VENDOR_8 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_8); +pub const AHARDWAREBUFFER_USAGE_VENDOR_9 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_9); +pub const AHARDWAREBUFFER_USAGE_VENDOR_10 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_10); +pub const AHARDWAREBUFFER_USAGE_VENDOR_11 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_11); +pub const AHARDWAREBUFFER_USAGE_VENDOR_12 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_12); +pub const AHARDWAREBUFFER_USAGE_VENDOR_13 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_13); +pub const AHARDWAREBUFFER_USAGE_VENDOR_14 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_14); +pub const AHARDWAREBUFFER_USAGE_VENDOR_15 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_15); +pub const AHARDWAREBUFFER_USAGE_VENDOR_16 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_16); +pub const AHARDWAREBUFFER_USAGE_VENDOR_17 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_17); +pub const AHARDWAREBUFFER_USAGE_VENDOR_18 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_18); +pub const AHARDWAREBUFFER_USAGE_VENDOR_19 = @enumToInt(enum_AHardwareBuffer_UsageFlags.AHARDWAREBUFFER_USAGE_VENDOR_19); +pub const enum_AHardwareBuffer_UsageFlags = enum(c_ulong) { + AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0, + AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2, + AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3, + AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 15, + AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0, + AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 32, + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 48, + AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 240, + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 256, + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 512, + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 512, + AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 2048, + AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 16384, + AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 65536, + AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 8388608, + AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 16777216, + AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 33554432, + AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 67108864, + AHARDWAREBUFFER_USAGE_VENDOR_0 = 268435456, + AHARDWAREBUFFER_USAGE_VENDOR_1 = 536870912, + AHARDWAREBUFFER_USAGE_VENDOR_2 = 1073741824, + AHARDWAREBUFFER_USAGE_VENDOR_3 = 2147483648, + AHARDWAREBUFFER_USAGE_VENDOR_4 = 281474976710656, + AHARDWAREBUFFER_USAGE_VENDOR_5 = 562949953421312, + AHARDWAREBUFFER_USAGE_VENDOR_6 = 1125899906842624, + AHARDWAREBUFFER_USAGE_VENDOR_7 = 2251799813685248, + AHARDWAREBUFFER_USAGE_VENDOR_8 = 4503599627370496, + AHARDWAREBUFFER_USAGE_VENDOR_9 = 9007199254740992, + AHARDWAREBUFFER_USAGE_VENDOR_10 = 18014398509481984, + AHARDWAREBUFFER_USAGE_VENDOR_11 = 36028797018963968, + AHARDWAREBUFFER_USAGE_VENDOR_12 = 72057594037927936, + AHARDWAREBUFFER_USAGE_VENDOR_13 = 144115188075855872, + AHARDWAREBUFFER_USAGE_VENDOR_14 = 288230376151711744, + AHARDWAREBUFFER_USAGE_VENDOR_15 = 576460752303423488, + AHARDWAREBUFFER_USAGE_VENDOR_16 = 1152921504606846976, + AHARDWAREBUFFER_USAGE_VENDOR_17 = 2305843009213693952, + AHARDWAREBUFFER_USAGE_VENDOR_18 = 4611686018427387904, + AHARDWAREBUFFER_USAGE_VENDOR_19 = 9223372036854775808, + _, +}; +pub const struct_AHardwareBuffer_Desc = extern struct { + width: u32, + height: u32, + layers: u32, + format: u32, + usage: u64, + stride: u32, + rfu0: u32, + rfu1: u64, +}; +pub const AHardwareBuffer_Desc = struct_AHardwareBuffer_Desc; +pub const struct_AHardwareBuffer_Plane = extern struct { + data: ?*anyopaque, + pixelStride: u32, + rowStride: u32, +}; +pub const AHardwareBuffer_Plane = struct_AHardwareBuffer_Plane; +pub const struct_AHardwareBuffer_Planes = extern struct { + planeCount: u32, + planes: [4]AHardwareBuffer_Plane, +}; +pub const AHardwareBuffer_Planes = struct_AHardwareBuffer_Planes; +pub const struct_AHardwareBuffer = opaque {}; +pub const AHardwareBuffer = struct_AHardwareBuffer; +pub extern fn AHardwareBuffer_allocate(desc: [*c]const AHardwareBuffer_Desc, outBuffer: [*c]?*AHardwareBuffer) c_int; +pub extern fn AHardwareBuffer_acquire(buffer: ?*AHardwareBuffer) void; +pub extern fn AHardwareBuffer_release(buffer: ?*AHardwareBuffer) void; +pub extern fn AHardwareBuffer_describe(buffer: ?*const AHardwareBuffer, outDesc: [*c]AHardwareBuffer_Desc) void; +pub extern fn AHardwareBuffer_lock(buffer: ?*AHardwareBuffer, usage: u64, fence: i32, rect: [*c]const ARect, outVirtualAddress: [*c]?*anyopaque) c_int; +pub extern fn AHardwareBuffer_lockPlanes(buffer: ?*AHardwareBuffer, usage: u64, fence: i32, rect: [*c]const ARect, outPlanes: [*c]AHardwareBuffer_Planes) c_int; +pub extern fn AHardwareBuffer_unlock(buffer: ?*AHardwareBuffer, fence: [*c]i32) c_int; +pub extern fn AHardwareBuffer_sendHandleToUnixSocket(buffer: ?*const AHardwareBuffer, socketFd: c_int) c_int; +pub extern fn AHardwareBuffer_recvHandleFromUnixSocket(socketFd: c_int, outBuffer: [*c]?*AHardwareBuffer) c_int; +pub extern fn AHardwareBuffer_isSupported(desc: [*c]const AHardwareBuffer_Desc) c_int; +pub extern fn AHardwareBuffer_lockAndGetInfo(buffer: ?*AHardwareBuffer, usage: u64, fence: i32, rect: [*c]const ARect, outVirtualAddress: [*c]?*anyopaque, outBytesPerPixel: [*c]i32, outBytesPerStride: [*c]i32) c_int; +pub const WINDOW_FORMAT_RGBA_8888 = @enumToInt(enum_ANativeWindow_LegacyFormat.WINDOW_FORMAT_RGBA_8888); +pub const WINDOW_FORMAT_RGBX_8888 = @enumToInt(enum_ANativeWindow_LegacyFormat.WINDOW_FORMAT_RGBX_8888); +pub const WINDOW_FORMAT_RGB_565 = @enumToInt(enum_ANativeWindow_LegacyFormat.WINDOW_FORMAT_RGB_565); +pub const enum_ANativeWindow_LegacyFormat = enum(c_int) { + WINDOW_FORMAT_RGBA_8888 = 1, + WINDOW_FORMAT_RGBX_8888 = 2, + WINDOW_FORMAT_RGB_565 = 4, + _, +}; +pub const ANATIVEWINDOW_TRANSFORM_IDENTITY = @enumToInt(enum_ANativeWindowTransform.ANATIVEWINDOW_TRANSFORM_IDENTITY); +pub const ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL = @enumToInt(enum_ANativeWindowTransform.ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL); +pub const ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL = @enumToInt(enum_ANativeWindowTransform.ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL); +pub const ANATIVEWINDOW_TRANSFORM_ROTATE_90 = @enumToInt(enum_ANativeWindowTransform.ANATIVEWINDOW_TRANSFORM_ROTATE_90); +pub const ANATIVEWINDOW_TRANSFORM_ROTATE_180 = @enumToInt(enum_ANativeWindowTransform.ANATIVEWINDOW_TRANSFORM_ROTATE_180); +pub const ANATIVEWINDOW_TRANSFORM_ROTATE_270 = @enumToInt(enum_ANativeWindowTransform.ANATIVEWINDOW_TRANSFORM_ROTATE_270); +pub const enum_ANativeWindowTransform = enum(c_int) { + ANATIVEWINDOW_TRANSFORM_IDENTITY = 0, + ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL = 1, + ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL = 2, + ANATIVEWINDOW_TRANSFORM_ROTATE_90 = 4, + ANATIVEWINDOW_TRANSFORM_ROTATE_180 = 3, + ANATIVEWINDOW_TRANSFORM_ROTATE_270 = 7, + _, +}; +pub const struct_ANativeWindow = opaque {}; +pub const ANativeWindow = struct_ANativeWindow; +pub const struct_ANativeWindow_Buffer = extern struct { + width: i32, + height: i32, + stride: i32, + format: i32, + bits: ?*anyopaque, + reserved: [6]u32, +}; +pub const ANativeWindow_Buffer = struct_ANativeWindow_Buffer; +pub extern fn ANativeWindow_acquire(window: ?*ANativeWindow) void; +pub extern fn ANativeWindow_release(window: ?*ANativeWindow) void; +pub extern fn ANativeWindow_getWidth(window: ?*ANativeWindow) i32; +pub extern fn ANativeWindow_getHeight(window: ?*ANativeWindow) i32; +pub extern fn ANativeWindow_getFormat(window: ?*ANativeWindow) i32; +pub extern fn ANativeWindow_setBuffersGeometry(window: ?*ANativeWindow, width: i32, height: i32, format: i32) i32; +pub extern fn ANativeWindow_lock(window: ?*ANativeWindow, outBuffer: [*c]ANativeWindow_Buffer, inOutDirtyBounds: [*c]ARect) i32; +pub extern fn ANativeWindow_unlockAndPost(window: ?*ANativeWindow) i32; +pub extern fn ANativeWindow_setBuffersTransform(window: ?*ANativeWindow, transform: i32) i32; +pub extern fn ANativeWindow_setBuffersDataSpace(window: ?*ANativeWindow, dataSpace: i32) i32; +pub extern fn ANativeWindow_getBuffersDataSpace(window: ?*ANativeWindow) i32; +pub const ANativeActivityCallbacks = extern struct { + onStart: ?*const fn (*ANativeActivity) callconv(.C) void, + onResume: ?*const fn (*ANativeActivity) callconv(.C) void, + onSaveInstanceState: ?*const fn (*ANativeActivity, *usize) callconv(.C) ?[*]u8, + onPause: ?*const fn (*ANativeActivity) callconv(.C) void, + onStop: ?*const fn (*ANativeActivity) callconv(.C) void, + onDestroy: ?*const fn (*ANativeActivity) callconv(.C) void, + onWindowFocusChanged: ?*const fn (*ANativeActivity, c_int) callconv(.C) void, + onNativeWindowCreated: ?*const fn (*ANativeActivity, *ANativeWindow) callconv(.C) void, + onNativeWindowResized: ?*const fn (*ANativeActivity, *ANativeWindow) callconv(.C) void, + onNativeWindowRedrawNeeded: ?*const fn (*ANativeActivity, *ANativeWindow) callconv(.C) void, + onNativeWindowDestroyed: ?*const fn (*ANativeActivity, *ANativeWindow) callconv(.C) void, + onInputQueueCreated: ?*const fn (*ANativeActivity, *AInputQueue) callconv(.C) void, + onInputQueueDestroyed: ?*const fn (*ANativeActivity, *AInputQueue) callconv(.C) void, + onContentRectChanged: ?*const fn (*ANativeActivity, *const ARect) callconv(.C) void, + onConfigurationChanged: ?*const fn (*ANativeActivity) callconv(.C) void, + onLowMemory: ?*const fn (*ANativeActivity) callconv(.C) void, +}; +pub const ANativeActivity = extern struct { + callbacks: *ANativeActivityCallbacks, + vm: *JavaVM, + env: *JNIEnv, + clazz: jobject, + internalDataPath: [*:0]const u8, + externalDataPath: [*:0]const u8, + sdkVersion: i32, + instance: ?*anyopaque, + assetManager: ?*AAssetManager, + obbPath: [*:0]const u8, +}; +pub const ANativeActivity_createFunc = *const fn ([*c]ANativeActivity, ?*anyopaque, usize) callconv(.C) void; + +pub extern fn ANativeActivity_finish(activity: [*c]ANativeActivity) void; +pub extern fn ANativeActivity_setWindowFormat(activity: [*c]ANativeActivity, format: i32) void; +pub extern fn ANativeActivity_setWindowFlags(activity: [*c]ANativeActivity, addFlags: u32, removeFlags: u32) void; +pub const ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT = @enumToInt(enum_unnamed_33.ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT); +pub const ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED = @enumToInt(enum_unnamed_33.ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); +const enum_unnamed_33 = enum(c_int) { + ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT = 1, + ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED = 2, + _, +}; +pub extern fn ANativeActivity_showSoftInput(activity: [*c]ANativeActivity, flags: u32) void; +pub const ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY = @enumToInt(enum_unnamed_34.ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY); +pub const ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS = @enumToInt(enum_unnamed_34.ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); +const enum_unnamed_34 = enum(c_int) { + ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY = 1, + ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS = 2, + _, +}; +pub extern fn ANativeActivity_hideSoftInput(activity: [*c]ANativeActivity, flags: u32) void; + +pub const __llvm__ = 1; +pub const __clang__ = 1; +pub const __clang_major__ = 10; +pub const __clang_minor__ = 0; +pub const __clang_patchlevel__ = 0; +pub const __clang_version__ = "10.0.0 "; +pub const __GNUC__ = 4; +pub const __GNUC_MINOR__ = 2; +pub const __GNUC_PATCHLEVEL__ = 1; +pub const __GXX_ABI_VERSION = 1002; +pub const __ATOMIC_RELAXED = 0; +pub const __ATOMIC_CONSUME = 1; +pub const __ATOMIC_ACQUIRE = 2; +pub const __ATOMIC_RELEASE = 3; +pub const __ATOMIC_ACQ_REL = 4; +pub const __ATOMIC_SEQ_CST = 5; +pub const __OPENCL_MEMORY_SCOPE_WORK_ITEM = 0; +pub const __OPENCL_MEMORY_SCOPE_WORK_GROUP = 1; +pub const __OPENCL_MEMORY_SCOPE_DEVICE = 2; +pub const __OPENCL_MEMORY_SCOPE_ALL_SVM_DEVICES = 3; +pub const __OPENCL_MEMORY_SCOPE_SUB_GROUP = 4; +pub const __PRAGMA_REDEFINE_EXTNAME = 1; +pub const __VERSION__ = "Clang 10.0.0 "; +pub const __OBJC_BOOL_IS_BOOL = 0; +pub const __CONSTANT_CFSTRINGS__ = 1; +pub const __OPTIMIZE__ = 1; +pub const __ORDER_LITTLE_ENDIAN__ = 1234; +pub const __ORDER_BIG_ENDIAN__ = 4321; +pub const __ORDER_PDP_ENDIAN__ = 3412; +pub const __BYTE_ORDER__ = __ORDER_LITTLE_ENDIAN__; +pub const __LITTLE_ENDIAN__ = 1; +pub const _LP64 = 1; +pub const __LP64__ = 1; +pub const __CHAR_BIT__ = 8; +pub const __SCHAR_MAX__ = 127; +pub const __SHRT_MAX__ = 32767; +pub const __INT_MAX__ = 2147483647; +pub const __LONG_MAX__ = @as(c_long, 9223372036854775807); +pub const __LONG_LONG_MAX__ = @as(c_longlong, 9223372036854775807); +pub const __WCHAR_MAX__ = @as(c_uint, 4294967295); +pub const __WINT_MAX__ = @as(c_uint, 4294967295); +pub const __INTMAX_MAX__ = @as(c_long, 9223372036854775807); +pub const __SIZE_MAX__ = @as(c_ulong, 18446744073709551615); +pub const __UINTMAX_MAX__ = @as(c_ulong, 18446744073709551615); +pub const __PTRDIFF_MAX__ = @as(c_long, 9223372036854775807); +pub const __INTPTR_MAX__ = @as(c_long, 9223372036854775807); +pub const __UINTPTR_MAX__ = @as(c_ulong, 18446744073709551615); +pub const __SIZEOF_DOUBLE__ = 8; +pub const __SIZEOF_FLOAT__ = 4; +pub const __SIZEOF_INT__ = 4; +pub const __SIZEOF_LONG__ = 8; +pub const __SIZEOF_LONG_DOUBLE__ = 16; +pub const __SIZEOF_LONG_LONG__ = 8; +pub const __SIZEOF_POINTER__ = 8; +pub const __SIZEOF_SHORT__ = 2; +pub const __SIZEOF_PTRDIFF_T__ = 8; +pub const __SIZEOF_SIZE_T__ = 8; +pub const __SIZEOF_WCHAR_T__ = 4; +pub const __SIZEOF_WINT_T__ = 4; +pub const __SIZEOF_INT128__ = 16; +pub const __INTMAX_FMTd__ = "ld"; +pub const __INTMAX_FMTi__ = "li"; +pub const __UINTMAX_FMTo__ = "lo"; +pub const __UINTMAX_FMTu__ = "lu"; +pub const __UINTMAX_FMTx__ = "lx"; +pub const __UINTMAX_FMTX__ = "lX"; +pub const __INTMAX_WIDTH__ = 64; +pub const __PTRDIFF_FMTd__ = "ld"; +pub const __PTRDIFF_FMTi__ = "li"; +pub const __PTRDIFF_WIDTH__ = 64; +pub const __INTPTR_FMTd__ = "ld"; +pub const __INTPTR_FMTi__ = "li"; +pub const __INTPTR_WIDTH__ = 64; +pub const __SIZE_FMTo__ = "lo"; +pub const __SIZE_FMTu__ = "lu"; +pub const __SIZE_FMTx__ = "lx"; +pub const __SIZE_FMTX__ = "lX"; +pub const __SIZE_WIDTH__ = 64; +pub const __WCHAR_WIDTH__ = 32; +pub const __WINT_WIDTH__ = 32; +pub const __SIG_ATOMIC_WIDTH__ = 32; +pub const __SIG_ATOMIC_MAX__ = 2147483647; +pub const __UINTMAX_WIDTH__ = 64; +pub const __UINTPTR_FMTo__ = "lo"; +pub const __UINTPTR_FMTu__ = "lu"; +pub const __UINTPTR_FMTx__ = "lx"; +pub const __UINTPTR_FMTX__ = "lX"; +pub const __UINTPTR_WIDTH__ = 64; +pub const __FLT16_HAS_DENORM__ = 1; +pub const __FLT16_DIG__ = 3; +pub const __FLT16_DECIMAL_DIG__ = 5; +pub const __FLT16_HAS_INFINITY__ = 1; +pub const __FLT16_HAS_QUIET_NAN__ = 1; +pub const __FLT16_MANT_DIG__ = 11; +pub const __FLT16_MAX_10_EXP__ = 4; +pub const __FLT16_MAX_EXP__ = 16; +pub const __FLT16_MIN_10_EXP__ = -4; +pub const __FLT16_MIN_EXP__ = -13; +pub const __FLT_DENORM_MIN__ = @as(f32, 1.40129846e-45); +pub const __FLT_HAS_DENORM__ = 1; +pub const __FLT_DIG__ = 6; +pub const __FLT_DECIMAL_DIG__ = 9; +pub const __FLT_EPSILON__ = @as(f32, 1.19209290e-7); +pub const __FLT_HAS_INFINITY__ = 1; +pub const __FLT_HAS_QUIET_NAN__ = 1; +pub const __FLT_MANT_DIG__ = 24; +pub const __FLT_MAX_10_EXP__ = 38; +pub const __FLT_MAX_EXP__ = 128; +pub const __FLT_MAX__ = @as(f32, 3.40282347e+38); +pub const __FLT_MIN_10_EXP__ = -37; +pub const __FLT_MIN_EXP__ = -125; +pub const __FLT_MIN__ = @as(f32, 1.17549435e-38); +pub const __DBL_DENORM_MIN__ = 4.9406564584124654e-324; +pub const __DBL_HAS_DENORM__ = 1; +pub const __DBL_DIG__ = 15; +pub const __DBL_DECIMAL_DIG__ = 17; +pub const __DBL_EPSILON__ = 2.2204460492503131e-16; +pub const __DBL_HAS_INFINITY__ = 1; +pub const __DBL_HAS_QUIET_NAN__ = 1; +pub const __DBL_MANT_DIG__ = 53; +pub const __DBL_MAX_10_EXP__ = 308; +pub const __DBL_MAX_EXP__ = 1024; +pub const __DBL_MAX__ = 1.7976931348623157e+308; +pub const __DBL_MIN_10_EXP__ = -307; +pub const __DBL_MIN_EXP__ = -1021; +pub const __DBL_MIN__ = 2.2250738585072014e-308; +pub const __LDBL_DENORM_MIN__ = @as(c_longdouble, 6.47517511943802511092443895822764655e-4966); +pub const __LDBL_HAS_DENORM__ = 1; +pub const __LDBL_DIG__ = 33; +pub const __LDBL_DECIMAL_DIG__ = 36; +pub const __LDBL_EPSILON__ = @as(c_longdouble, 1.92592994438723585305597794258492732e-34); +pub const __LDBL_HAS_INFINITY__ = 1; +pub const __LDBL_HAS_QUIET_NAN__ = 1; +pub const __LDBL_MANT_DIG__ = 113; +pub const __LDBL_MAX_10_EXP__ = 4932; +pub const __LDBL_MAX_EXP__ = 16384; +pub const __LDBL_MAX__ = @as(c_longdouble, 1.18973149535723176508575932662800702e+4932); +pub const __LDBL_MIN_10_EXP__ = -4931; +pub const __LDBL_MIN_EXP__ = -16381; +pub const __LDBL_MIN__ = @as(c_longdouble, 3.36210314311209350626267781732175260e-4932); +pub const __POINTER_WIDTH__ = 64; +pub const __BIGGEST_ALIGNMENT__ = 16; +pub const __CHAR_UNSIGNED__ = 1; +pub const __WCHAR_UNSIGNED__ = 1; +pub const __WINT_UNSIGNED__ = 1; +pub const __INT8_FMTd__ = "hhd"; +pub const __INT8_FMTi__ = "hhi"; +pub const __INT16_TYPE__ = c_short; +pub const __INT16_FMTd__ = "hd"; +pub const __INT16_FMTi__ = "hi"; +pub const __INT32_TYPE__ = c_int; +pub const __INT32_FMTd__ = "d"; +pub const __INT32_FMTi__ = "i"; +pub const __INT64_FMTd__ = "ld"; +pub const __INT64_FMTi__ = "li"; +pub const __UINT8_FMTo__ = "hho"; +pub const __UINT8_FMTu__ = "hhu"; +pub const __UINT8_FMTx__ = "hhx"; +pub const __UINT8_FMTX__ = "hhX"; +pub const __UINT8_MAX__ = 255; +pub const __INT8_MAX__ = 127; +pub const __UINT16_FMTo__ = "ho"; +pub const __UINT16_FMTu__ = "hu"; +pub const __UINT16_FMTx__ = "hx"; +pub const __UINT16_FMTX__ = "hX"; +pub const __UINT16_MAX__ = 65535; +pub const __INT16_MAX__ = 32767; +pub const __UINT32_FMTo__ = "o"; +pub const __UINT32_FMTu__ = "u"; +pub const __UINT32_FMTx__ = "x"; +pub const __UINT32_FMTX__ = "X"; +pub const __UINT32_MAX__ = @as(c_uint, 4294967295); +pub const __INT32_MAX__ = 2147483647; +pub const __UINT64_FMTo__ = "lo"; +pub const __UINT64_FMTu__ = "lu"; +pub const __UINT64_FMTx__ = "lx"; +pub const __UINT64_FMTX__ = "lX"; +pub const __UINT64_MAX__ = @as(c_ulong, 18446744073709551615); +pub const __INT64_MAX__ = @as(c_long, 9223372036854775807); +pub const __INT_LEAST8_MAX__ = 127; +pub const __INT_LEAST8_FMTd__ = "hhd"; +pub const __INT_LEAST8_FMTi__ = "hhi"; +pub const __UINT_LEAST8_MAX__ = 255; +pub const __UINT_LEAST8_FMTo__ = "hho"; +pub const __UINT_LEAST8_FMTu__ = "hhu"; +pub const __UINT_LEAST8_FMTx__ = "hhx"; +pub const __UINT_LEAST8_FMTX__ = "hhX"; +pub const __INT_LEAST16_TYPE__ = c_short; +pub const __INT_LEAST16_MAX__ = 32767; +pub const __INT_LEAST16_FMTd__ = "hd"; +pub const __INT_LEAST16_FMTi__ = "hi"; +pub const __UINT_LEAST16_MAX__ = 65535; +pub const __UINT_LEAST16_FMTo__ = "ho"; +pub const __UINT_LEAST16_FMTu__ = "hu"; +pub const __UINT_LEAST16_FMTx__ = "hx"; +pub const __UINT_LEAST16_FMTX__ = "hX"; +pub const __INT_LEAST32_TYPE__ = c_int; +pub const __INT_LEAST32_MAX__ = 2147483647; +pub const __INT_LEAST32_FMTd__ = "d"; +pub const __INT_LEAST32_FMTi__ = "i"; +pub const __UINT_LEAST32_MAX__ = @as(c_uint, 4294967295); +pub const __UINT_LEAST32_FMTo__ = "o"; +pub const __UINT_LEAST32_FMTu__ = "u"; +pub const __UINT_LEAST32_FMTx__ = "x"; +pub const __UINT_LEAST32_FMTX__ = "X"; +pub const __INT_LEAST64_MAX__ = @as(c_long, 9223372036854775807); +pub const __INT_LEAST64_FMTd__ = "ld"; +pub const __INT_LEAST64_FMTi__ = "li"; +pub const __UINT_LEAST64_MAX__ = @as(c_ulong, 18446744073709551615); +pub const __UINT_LEAST64_FMTo__ = "lo"; +pub const __UINT_LEAST64_FMTu__ = "lu"; +pub const __UINT_LEAST64_FMTx__ = "lx"; +pub const __UINT_LEAST64_FMTX__ = "lX"; +pub const __INT_FAST8_MAX__ = 127; +pub const __INT_FAST8_FMTd__ = "hhd"; +pub const __INT_FAST8_FMTi__ = "hhi"; +pub const __UINT_FAST8_MAX__ = 255; +pub const __UINT_FAST8_FMTo__ = "hho"; +pub const __UINT_FAST8_FMTu__ = "hhu"; +pub const __UINT_FAST8_FMTx__ = "hhx"; +pub const __UINT_FAST8_FMTX__ = "hhX"; +pub const __INT_FAST16_TYPE__ = c_short; +pub const __INT_FAST16_MAX__ = 32767; +pub const __INT_FAST16_FMTd__ = "hd"; +pub const __INT_FAST16_FMTi__ = "hi"; +pub const __UINT_FAST16_MAX__ = 65535; +pub const __UINT_FAST16_FMTo__ = "ho"; +pub const __UINT_FAST16_FMTu__ = "hu"; +pub const __UINT_FAST16_FMTx__ = "hx"; +pub const __UINT_FAST16_FMTX__ = "hX"; +pub const __INT_FAST32_TYPE__ = c_int; +pub const __INT_FAST32_MAX__ = 2147483647; +pub const __INT_FAST32_FMTd__ = "d"; +pub const __INT_FAST32_FMTi__ = "i"; +pub const __UINT_FAST32_MAX__ = @as(c_uint, 4294967295); +pub const __UINT_FAST32_FMTo__ = "o"; +pub const __UINT_FAST32_FMTu__ = "u"; +pub const __UINT_FAST32_FMTx__ = "x"; +pub const __UINT_FAST32_FMTX__ = "X"; +pub const __INT_FAST64_MAX__ = @as(c_long, 9223372036854775807); +pub const __INT_FAST64_FMTd__ = "ld"; +pub const __INT_FAST64_FMTi__ = "li"; +pub const __UINT_FAST64_MAX__ = @as(c_ulong, 18446744073709551615); +pub const __UINT_FAST64_FMTo__ = "lo"; +pub const __UINT_FAST64_FMTu__ = "lu"; +pub const __UINT_FAST64_FMTx__ = "lx"; +pub const __UINT_FAST64_FMTX__ = "lX"; +pub const __FINITE_MATH_ONLY__ = 0; +pub const __GNUC_STDC_INLINE__ = 1; +pub const __GCC_ATOMIC_TEST_AND_SET_TRUEVAL = 1; +pub const __CLANG_ATOMIC_BOOL_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_CHAR_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_CHAR16_T_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_CHAR32_T_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_WCHAR_T_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_SHORT_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_INT_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_LONG_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_LLONG_LOCK_FREE = 2; +pub const __CLANG_ATOMIC_POINTER_LOCK_FREE = 2; +pub const __GCC_ATOMIC_BOOL_LOCK_FREE = 2; +pub const __GCC_ATOMIC_CHAR_LOCK_FREE = 2; +pub const __GCC_ATOMIC_CHAR16_T_LOCK_FREE = 2; +pub const __GCC_ATOMIC_CHAR32_T_LOCK_FREE = 2; +pub const __GCC_ATOMIC_WCHAR_T_LOCK_FREE = 2; +pub const __GCC_ATOMIC_SHORT_LOCK_FREE = 2; +pub const __GCC_ATOMIC_INT_LOCK_FREE = 2; +pub const __GCC_ATOMIC_LONG_LOCK_FREE = 2; +pub const __GCC_ATOMIC_LLONG_LOCK_FREE = 2; +pub const __GCC_ATOMIC_POINTER_LOCK_FREE = 2; +pub const __PIC__ = 2; +pub const __pic__ = 2; +pub const __FLT_EVAL_METHOD__ = 0; +pub const __FLT_RADIX__ = 2; +pub const __DECIMAL_DIG__ = __LDBL_DECIMAL_DIG__; +pub const __SSP_STRONG__ = 2; +pub const __AARCH64EL__ = 1; +pub const __aarch64__ = 1; +pub const __ARM_ACLE = 200; +pub const __ARM_ARCH = 8; +pub const __ARM_ARCH_PROFILE = 'A'; +pub const __ARM_64BIT_STATE = 1; +pub const __ARM_PCS_AAPCS64 = 1; +pub const __ARM_ARCH_ISA_A64 = 1; +pub const __ARM_FEATURE_CLZ = 1; +pub const __ARM_FEATURE_FMA = 1; +pub const __ARM_FEATURE_LDREX = 0xF; +pub const __ARM_FEATURE_IDIV = 1; +pub const __ARM_FEATURE_DIV = 1; +pub const __ARM_FEATURE_NUMERIC_MAXMIN = 1; +pub const __ARM_FEATURE_DIRECTED_ROUNDING = 1; +pub const __ARM_ALIGN_MAX_STACK_PWR = 4; +pub const __ARM_FP = 0xE; +pub const __ARM_FP16_FORMAT_IEEE = 1; +pub const __ARM_FP16_ARGS = 1; +pub const __ARM_SIZEOF_WCHAR_T = 4; +pub const __ARM_SIZEOF_MINIMAL_ENUM = 4; +pub const __ARM_NEON = 1; +pub const __ARM_NEON_FP = 0xE; +pub const __ARM_FEATURE_UNALIGNED = 1; +pub const __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 = 1; +pub const __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 = 1; +pub const __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 = 1; +pub const __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 = 1; +pub const unix = 1; +pub const __unix = 1; +pub const __unix__ = 1; +pub const linux = 1; +pub const __linux = 1; +pub const __linux__ = 1; +pub const __ELF__ = 1; +pub const __gnu_linux__ = 1; +pub const __STDC__ = 1; +pub const __STDC_HOSTED__ = 1; +pub const __STDC_VERSION__ = @as(c_long, 201112); +pub const __STDC_UTF_16__ = 1; +pub const __STDC_UTF_32__ = 1; +pub const _DEBUG = 1; +pub const ANDROID = 1; +pub const APPNAME = "ziggy"; +pub const DANDROIDVERSION = 29; + +pub const __GNUC_VA_LIST = 1; +pub const __BIONIC__ = 1; +pub inline fn __BIONIC_CAST(_: anytype, _t: anytype, _v: anytype) @TypeOf((@import("std").meta.cast(_t, _v))) { + return (@import("std").meta.cast(_t, _v)); +} +pub inline fn __BIONIC_ALIGN(__value: anytype, __alignment: anytype) @TypeOf((__value + (__alignment - 1)) & ~__alignment - 1) { + return (__value + (__alignment - 1)) & ~__alignment - 1; +} +pub inline fn __P(protos: anytype) @TypeOf(protos) { + return protos; +} + +pub const __WORDSIZE = 64; +pub const __bos_level = 0; +pub const __ANDROID_API_FUTURE__ = 10000; +pub const __ANDROID_API__ = __ANDROID_API_FUTURE__; +pub const __ANDROID_API_G__ = 9; +pub const __ANDROID_API_I__ = 14; +pub const __ANDROID_API_J__ = 16; +pub const __ANDROID_API_J_MR1__ = 17; +pub const __ANDROID_API_J_MR2__ = 18; +pub const __ANDROID_API_K__ = 19; +pub const __ANDROID_API_L__ = 21; +pub const __ANDROID_API_L_MR1__ = 22; +pub const __ANDROID_API_M__ = 23; +pub const __ANDROID_API_N__ = 24; +pub const __ANDROID_API_N_MR1__ = 25; +pub const __ANDROID_API_O__ = 26; +pub const __ANDROID_API_O_MR1__ = 27; +pub const __ANDROID_API_P__ = 28; +pub const __ANDROID_API_Q__ = 29; +pub const __ANDROID_API_R__ = 30; +pub const __NDK_MAJOR__ = 21; +pub const __NDK_MINOR__ = 1; +pub const __NDK_BETA__ = 0; +pub const __NDK_BUILD__ = 6352462; +pub const __NDK_CANARY__ = 0; +pub const NULL = (@import("std").meta.cast(?*anyopaque, 0)); + +pub const WCHAR_MAX = __WCHAR_MAX__; +pub const WCHAR_MIN = '\x00'; + +pub const INT8_MIN = -128; +pub const INT8_MAX = 127; +pub const INT_LEAST8_MIN = INT8_MIN; +pub const INT_LEAST8_MAX = INT8_MAX; +pub const INT_FAST8_MIN = INT8_MIN; +pub const INT_FAST8_MAX = INT8_MAX; +pub const UINT8_MAX = 255; +pub const UINT_LEAST8_MAX = UINT8_MAX; +pub const UINT_FAST8_MAX = UINT8_MAX; +pub const INT16_MIN = -32768; +pub const INT16_MAX = 32767; +pub const INT_LEAST16_MIN = INT16_MIN; +pub const INT_LEAST16_MAX = INT16_MAX; +pub const INT_FAST16_MIN = INT32_MIN; +pub const INT_FAST16_MAX = INT32_MAX; +pub const UINT16_MAX = 65535; +pub const UINT_LEAST16_MAX = UINT16_MAX; +pub const UINT_FAST16_MAX = UINT32_MAX; +pub const INT32_MIN = -2147483647 - 1; +pub const INT32_MAX = 2147483647; +pub const INT_LEAST32_MIN = INT32_MIN; +pub const INT_LEAST32_MAX = INT32_MAX; +pub const INT_FAST32_MIN = INT32_MIN; +pub const INT_FAST32_MAX = INT32_MAX; +pub const UINT32_MAX = @as(c_uint, 4294967295); + +pub const __BITS_PER_LONG = 64; +pub const __FD_SETSIZE = 1024; +pub const JNI_FALSE = 0; +pub const JNI_TRUE = 1; +pub const JNI_VERSION_1_1 = 0x00010001; +pub const JNI_VERSION_1_2 = 0x00010002; +pub const JNI_VERSION_1_4 = 0x00010004; +pub const JNI_VERSION_1_6 = 0x00010006; +pub const JNI_OK = 0; +pub const JNI_ERR = -1; +pub const JNI_EDETACHED = -2; +pub const JNI_EVERSION = -3; +pub const JNI_ENOMEM = -4; +pub const JNI_EEXIST = -5; +pub const JNI_EINVAL = -6; +pub const JNI_COMMIT = 1; +pub const JNI_ABORT = 2; +pub const AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT = 8; +pub const __PRI_64_prefix = "l"; +pub const __PRI_PTR_prefix = "l"; +pub const __PRI_FAST_prefix = __PRI_PTR_prefix; +pub const PRId8 = "d"; +pub const PRId16 = "d"; +pub const PRId32 = "d"; +pub const PRId64 = __PRI_64_prefix ++ "d"; +pub const PRIdLEAST8 = "d"; +pub const PRIdLEAST16 = "d"; +pub const PRIdLEAST32 = "d"; +pub const PRIdLEAST64 = __PRI_64_prefix ++ "d"; +pub const PRIdFAST8 = "d"; +pub const PRIdFAST16 = __PRI_FAST_prefix ++ "d"; +pub const PRIdFAST32 = __PRI_FAST_prefix ++ "d"; +pub const PRIdFAST64 = __PRI_64_prefix ++ "d"; +pub const PRIdMAX = "jd"; +pub const PRIdPTR = __PRI_PTR_prefix ++ "d"; +pub const PRIi8 = "i"; +pub const PRIi16 = "i"; +pub const PRIi32 = "i"; +pub const PRIi64 = __PRI_64_prefix ++ "i"; +pub const PRIiLEAST8 = "i"; +pub const PRIiLEAST16 = "i"; +pub const PRIiLEAST32 = "i"; +pub const PRIiLEAST64 = __PRI_64_prefix ++ "i"; +pub const PRIiFAST8 = "i"; +pub const PRIiFAST16 = __PRI_FAST_prefix ++ "i"; +pub const PRIiFAST32 = __PRI_FAST_prefix ++ "i"; +pub const PRIiFAST64 = __PRI_64_prefix ++ "i"; +pub const PRIiMAX = "ji"; +pub const PRIiPTR = __PRI_PTR_prefix ++ "i"; +pub const PRIo8 = "o"; +pub const PRIo16 = "o"; +pub const PRIo32 = "o"; +pub const PRIo64 = __PRI_64_prefix ++ "o"; +pub const PRIoLEAST8 = "o"; +pub const PRIoLEAST16 = "o"; +pub const PRIoLEAST32 = "o"; +pub const PRIoLEAST64 = __PRI_64_prefix ++ "o"; +pub const PRIoFAST8 = "o"; +pub const PRIoFAST16 = __PRI_FAST_prefix ++ "o"; +pub const PRIoFAST32 = __PRI_FAST_prefix ++ "o"; +pub const PRIoFAST64 = __PRI_64_prefix ++ "o"; +pub const PRIoMAX = "jo"; +pub const PRIoPTR = __PRI_PTR_prefix ++ "o"; +pub const PRIu8 = "u"; +pub const PRIu16 = "u"; +pub const PRIu32 = "u"; +pub const PRIu64 = __PRI_64_prefix ++ "u"; +pub const PRIuLEAST8 = "u"; +pub const PRIuLEAST16 = "u"; +pub const PRIuLEAST32 = "u"; +pub const PRIuLEAST64 = __PRI_64_prefix ++ "u"; +pub const PRIuFAST8 = "u"; +pub const PRIuFAST16 = __PRI_FAST_prefix ++ "u"; +pub const PRIuFAST32 = __PRI_FAST_prefix ++ "u"; +pub const PRIuFAST64 = __PRI_64_prefix ++ "u"; +pub const PRIuMAX = "ju"; +pub const PRIuPTR = __PRI_PTR_prefix ++ "u"; +pub const PRIx8 = "x"; +pub const PRIx16 = "x"; +pub const PRIx32 = "x"; +pub const PRIx64 = __PRI_64_prefix ++ "x"; +pub const PRIxLEAST8 = "x"; +pub const PRIxLEAST16 = "x"; +pub const PRIxLEAST32 = "x"; +pub const PRIxLEAST64 = __PRI_64_prefix ++ "x"; +pub const PRIxFAST8 = "x"; +pub const PRIxFAST16 = __PRI_FAST_prefix ++ "x"; +pub const PRIxFAST32 = __PRI_FAST_prefix ++ "x"; +pub const PRIxFAST64 = __PRI_64_prefix ++ "x"; +pub const PRIxMAX = "jx"; +pub const PRIxPTR = __PRI_PTR_prefix ++ "x"; +pub const PRIX8 = "X"; +pub const PRIX16 = "X"; +pub const PRIX32 = "X"; +pub const PRIX64 = __PRI_64_prefix ++ "X"; +pub const PRIXLEAST8 = "X"; +pub const PRIXLEAST16 = "X"; +pub const PRIXLEAST32 = "X"; +pub const PRIXLEAST64 = __PRI_64_prefix ++ "X"; +pub const PRIXFAST8 = "X"; +pub const PRIXFAST16 = __PRI_FAST_prefix ++ "X"; +pub const PRIXFAST32 = __PRI_FAST_prefix ++ "X"; +pub const PRIXFAST64 = __PRI_64_prefix ++ "X"; +pub const PRIXMAX = "jX"; +pub const PRIXPTR = __PRI_PTR_prefix ++ "X"; +pub const SCNd8 = "hhd"; +pub const SCNd16 = "hd"; +pub const SCNd32 = "d"; +pub const SCNd64 = __PRI_64_prefix ++ "d"; +pub const SCNdLEAST8 = "hhd"; +pub const SCNdLEAST16 = "hd"; +pub const SCNdLEAST32 = "d"; +pub const SCNdLEAST64 = __PRI_64_prefix ++ "d"; +pub const SCNdFAST8 = "hhd"; +pub const SCNdFAST16 = __PRI_FAST_prefix ++ "d"; +pub const SCNdFAST32 = __PRI_FAST_prefix ++ "d"; +pub const SCNdFAST64 = __PRI_64_prefix ++ "d"; +pub const SCNdMAX = "jd"; +pub const SCNdPTR = __PRI_PTR_prefix ++ "d"; +pub const SCNi8 = "hhi"; +pub const SCNi16 = "hi"; +pub const SCNi32 = "i"; +pub const SCNi64 = __PRI_64_prefix ++ "i"; +pub const SCNiLEAST8 = "hhi"; +pub const SCNiLEAST16 = "hi"; +pub const SCNiLEAST32 = "i"; +pub const SCNiLEAST64 = __PRI_64_prefix ++ "i"; +pub const SCNiFAST8 = "hhi"; +pub const SCNiFAST16 = __PRI_FAST_prefix ++ "i"; +pub const SCNiFAST32 = __PRI_FAST_prefix ++ "i"; +pub const SCNiFAST64 = __PRI_64_prefix ++ "i"; +pub const SCNiMAX = "ji"; +pub const SCNiPTR = __PRI_PTR_prefix ++ "i"; +pub const SCNo8 = "hho"; +pub const SCNo16 = "ho"; +pub const SCNo32 = "o"; +pub const SCNo64 = __PRI_64_prefix ++ "o"; +pub const SCNoLEAST8 = "hho"; +pub const SCNoLEAST16 = "ho"; +pub const SCNoLEAST32 = "o"; +pub const SCNoLEAST64 = __PRI_64_prefix ++ "o"; +pub const SCNoFAST8 = "hho"; +pub const SCNoFAST16 = __PRI_FAST_prefix ++ "o"; +pub const SCNoFAST32 = __PRI_FAST_prefix ++ "o"; +pub const SCNoFAST64 = __PRI_64_prefix ++ "o"; +pub const SCNoMAX = "jo"; +pub const SCNoPTR = __PRI_PTR_prefix ++ "o"; +pub const SCNu8 = "hhu"; +pub const SCNu16 = "hu"; +pub const SCNu32 = "u"; +pub const SCNu64 = __PRI_64_prefix ++ "u"; +pub const SCNuLEAST8 = "hhu"; +pub const SCNuLEAST16 = "hu"; +pub const SCNuLEAST32 = "u"; +pub const SCNuLEAST64 = __PRI_64_prefix ++ "u"; +pub const SCNuFAST8 = "hhu"; +pub const SCNuFAST16 = __PRI_FAST_prefix ++ "u"; +pub const SCNuFAST32 = __PRI_FAST_prefix ++ "u"; +pub const SCNuFAST64 = __PRI_64_prefix ++ "u"; +pub const SCNuMAX = "ju"; +pub const SCNuPTR = __PRI_PTR_prefix ++ "u"; +pub const SCNx8 = "hhx"; +pub const SCNx16 = "hx"; +pub const SCNx32 = "x"; +pub const SCNx64 = __PRI_64_prefix ++ "x"; +pub const SCNxLEAST8 = "hhx"; +pub const SCNxLEAST16 = "hx"; +pub const SCNxLEAST32 = "x"; +pub const SCNxLEAST64 = __PRI_64_prefix ++ "x"; +pub const SCNxFAST8 = "hhx"; +pub const SCNxFAST16 = __PRI_FAST_prefix ++ "x"; +pub const SCNxFAST32 = __PRI_FAST_prefix ++ "x"; +pub const SCNxFAST64 = __PRI_64_prefix ++ "x"; +pub const SCNxMAX = "jx"; +pub const SCNxPTR = __PRI_PTR_prefix ++ "x"; +pub const log_id = enum_log_id; +pub const _jfieldID = struct__jfieldID; +pub const _jmethodID = struct__jmethodID; +pub const JNIInvokeInterface = struct_JNIInvokeInterface; +pub const _JNIEnv = struct__JNIEnv; +pub const _JavaVM = struct__JavaVM; +pub const ADataSpace = enum_ADataSpace; +pub const AHardwareBuffer_Format = enum_AHardwareBuffer_Format; +pub const AHardwareBuffer_UsageFlags = enum_AHardwareBuffer_UsageFlags; +pub const ANativeWindow_LegacyFormat = enum_ANativeWindow_LegacyFormat; +pub const ANativeWindowTransform = enum_ANativeWindowTransform; + +pub extern fn __system_property_get(name: [*:0]const u8, value: [*]u8) callconv(.C) c_int; diff --git a/android/src/android-support.zig b/android/src/android-support.zig new file mode 100644 index 00000000..6306c357 --- /dev/null +++ b/android/src/android-support.zig @@ -0,0 +1,378 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const c = @import("c.zig"); + +const android = @import("android-bind.zig"); +const build_options = @import("build_options"); + +pub const egl = @import("egl.zig"); +pub const JNI = @import("jni.zig").JNI; +pub const audio = @import("audio.zig"); + +const app_log = std.log.scoped(.app_glue); + +// Export the flat functions for now +// pub const native = android; +pub usingnamespace android; + +const AndroidApp = @import("root").AndroidApp; + +pub var sdk_version: c_int = 0; + +/// Actual application entry point +export fn ANativeActivity_onCreate(activity: *android.ANativeActivity, savedState: ?[*]u8, savedStateSize: usize) callconv(.C) void { + { + var sdk_ver_str: [92]u8 = undefined; + const len = android.__system_property_get("ro.build.version.sdk", &sdk_ver_str); + if (len <= 0) { + sdk_version = 0; + } else { + const str = sdk_ver_str[0..@intCast(usize, len)]; + sdk_version = std.fmt.parseInt(c_int, str, 10) catch 0; + } + } + + app_log.debug( + \\Zig Android SDK: + \\ App: {s} + \\ API level: target={d}, actual={d} + \\ App pid: {} + \\ Build mode: {s} + \\ ABI: {s}-{s}-{s} + \\ Compiler version: {} + \\ Compiler backend: {s} + , .{ + build_options.app_name, + build_options.android_sdk_version, + sdk_version, + std.os.linux.getpid(), + @tagName(builtin.mode), + @tagName(builtin.cpu.arch), + @tagName(builtin.os.tag), + @tagName(builtin.abi), + builtin.zig_version, + @tagName(builtin.zig_backend), + }); + + const app = std.heap.c_allocator.create(AndroidApp) catch { + app_log.err("Could not create new AndroidApp: OutOfMemory!\n", .{}); + return; + }; + + activity.callbacks.* = makeNativeActivityGlue(AndroidApp); + + app.* = AndroidApp.init( + std.heap.c_allocator, + activity, + if (savedState) |state| + state[0..savedStateSize] + else + null, + ) catch |err| { + std.err("Failed to restore app state: {}\n", .{err}); + std.heap.c_allocator.destroy(app); + return; + }; + + app.start() catch |err| { + std.log.err("Failed to start app state: {}\n", .{err}); + app.deinit(); + std.heap.c_allocator.destroy(app); + return; + }; + + activity.instance = app; + + app_log.debug("Successfully started the app.\n", .{}); +} + +// // Required by C code for now… +threadlocal var errno: c_int = 0; +export fn __errno_location() *c_int { + return &errno; +} + +var recursive_panic = false; + +// Android Panic implementation +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + var logger = LogWriter{ .log_level = android.ANDROID_LOG_ERROR }; + + if (@atomicLoad(bool, &recursive_panic, .SeqCst)) { + logger.writer().print("RECURSIVE PANIC: {s}\n", .{message}) catch {}; + while (true) { + std.time.sleep(std.time.ns_per_week); + } + } + + @atomicStore(bool, &recursive_panic, true, .SeqCst); + + logger.writer().print("PANIC: {s}\n", .{message}) catch {}; + + const maybe_debug_info = std.debug.getSelfDebugInfo() catch null; + + // dumpStackTrace(maybe_debug_info); + + { + var count: usize = 0; + var it = std.debug.StackIterator.init(null, null); + while (it.next()) |return_address| { + printSymbolInfoAt(count, maybe_debug_info, return_address); + count += 1; + } + } + + _ = stack_trace; + // if (stack_trace) |st| { + // logger.writer().print("{}\n", .{st}) catch {}; + // } + + // if (std.debug.getSelfDebugInfo()) |debug_info| { + // std.debug.writeCurrentStackTrace(logger.writer(), debug_info, .no_color, null) catch |err| { + // logger.writer().print("failed to write stack trace: {s}\n", .{err}) catch {}; + // }; + // } else |err| { + // logger.writer().print("failed to get debug info: {s}\n", .{err}) catch {}; + // } + + logger.writer().writeAll("<-- end of stack trace -->\n") catch {}; + + std.os.exit(1); +} + +const LogWriter = struct { + log_level: c_int, + + line_buffer: [8192]u8 = undefined, + line_len: usize = 0, + + const Error = error{}; + const Writer = std.io.Writer(*LogWriter, Error, write); + + fn write(self: *LogWriter, buffer: []const u8) Error!usize { + for (buffer) |char| { + switch (char) { + '\n' => { + self.flush(); + }, + else => { + if (self.line_len >= self.line_buffer.len - 1) { + self.flush(); + } + self.line_buffer[self.line_len] = char; + self.line_len += 1; + }, + } + } + return buffer.len; + } + + fn flush(self: *LogWriter) void { + if (self.line_len > 0) { + // var buf = std.mem.zeroes([129]u8); + // const msg = std.fmt.bufPrint(&buf, "PRINT({})\x00", .{self.line_len}) catch "PRINT(???)"; + // _ = android.__android_log_write( + // self.log_level, + // build_options.app_name.ptr, + // msg.ptr, + // ); + + std.debug.assert(self.line_len < self.line_buffer.len - 1); + self.line_buffer[self.line_len] = 0; + _ = android.__android_log_write( + self.log_level, + build_options.app_name.ptr, + &self.line_buffer, + ); + } + self.line_len = 0; + } + + fn writer(self: *LogWriter) Writer { + return Writer{ .context = self }; + } +}; + +// Android Logging implementation +pub fn log( + comptime message_level: std.log.Level, + comptime scope: @Type(.EnumLiteral), + comptime format: []const u8, + args: anytype, +) void { + const level = switch (message_level) { + // => .ANDROID_LOG_VERBOSE, + .debug => android.ANDROID_LOG_DEBUG, + .info => android.ANDROID_LOG_INFO, + .warn => android.ANDROID_LOG_WARN, + .err => android.ANDROID_LOG_ERROR, + }; + + var logger = LogWriter{ + .log_level = level, + }; + defer logger.flush(); + + logger.writer().print("{s}: " ++ format, .{@tagName(scope)} ++ args) catch {}; +} + +/// Returns a wrapper implementation for the given App type which implements all +/// ANativeActivity callbacks. +fn makeNativeActivityGlue(comptime App: type) android.ANativeActivityCallbacks { + const T = struct { + fn invoke(activity: *android.ANativeActivity, comptime func: []const u8, args: anytype) void { + if (@hasDecl(App, func)) { + if (activity.instance) |instance| { + const result = @call(.{}, @field(App, func), .{@ptrCast(*App, @alignCast(@alignOf(App), instance))} ++ args); + switch (@typeInfo(@TypeOf(result))) { + .ErrorUnion => result catch |err| app_log.emerg("{s} returned error {s}", .{ func, @errorName(err) }), + .Void => {}, + .ErrorSet => app_log.emerg("{s} returned error {s}", .{ func, @errorName(result) }), + else => @compileError("callback must return void!"), + } + } + } else { + app_log.debug("ANativeActivity callback {s} not available on {s}", .{ func, @typeName(App) }); + } + } + + // return value must be created with malloc(), so we pass the c_allocator to App.onSaveInstanceState + fn onSaveInstanceState(activity: *android.ANativeActivity, outSize: *usize) callconv(.C) ?[*]u8 { + outSize.* = 0; + if (@hasDecl(App, "onSaveInstanceState")) { + if (activity.instance) |instance| { + const optional_slice = @ptrCast(*App, @alignCast(@alignOf(App), instance)).onSaveInstanceState(std.heap.c_allocator); + if (optional_slice) |slice| { + outSize.* = slice.len; + return slice.ptr; + } + } + } else { + app_log.debug("ANativeActivity callback onSaveInstanceState not available on {s}", .{@typeName(App)}); + } + return null; + } + + fn onDestroy(activity: *android.ANativeActivity) callconv(.C) void { + if (activity.instance) |instance| { + const app = @ptrCast(*App, @alignCast(@alignOf(App), instance)); + app.deinit(); + std.heap.c_allocator.destroy(app); + } + } + fn onStart(activity: *android.ANativeActivity) callconv(.C) void { + invoke(activity, "onStart", .{}); + } + fn onResume(activity: *android.ANativeActivity) callconv(.C) void { + invoke(activity, "onResume", .{}); + } + fn onPause(activity: *android.ANativeActivity) callconv(.C) void { + invoke(activity, "onPause", .{}); + } + fn onStop(activity: *android.ANativeActivity) callconv(.C) void { + invoke(activity, "onStop", .{}); + } + fn onConfigurationChanged(activity: *android.ANativeActivity) callconv(.C) void { + invoke(activity, "onConfigurationChanged", .{}); + } + fn onLowMemory(activity: *android.ANativeActivity) callconv(.C) void { + invoke(activity, "onLowMemory", .{}); + } + fn onWindowFocusChanged(activity: *android.ANativeActivity, hasFocus: c_int) callconv(.C) void { + invoke(activity, "onWindowFocusChanged", .{(hasFocus != 0)}); + } + fn onNativeWindowCreated(activity: *android.ANativeActivity, window: *android.ANativeWindow) callconv(.C) void { + invoke(activity, "onNativeWindowCreated", .{window}); + } + fn onNativeWindowResized(activity: *android.ANativeActivity, window: *android.ANativeWindow) callconv(.C) void { + invoke(activity, "onNativeWindowResized", .{window}); + } + fn onNativeWindowRedrawNeeded(activity: *android.ANativeActivity, window: *android.ANativeWindow) callconv(.C) void { + invoke(activity, "onNativeWindowRedrawNeeded", .{window}); + } + fn onNativeWindowDestroyed(activity: *android.ANativeActivity, window: *android.ANativeWindow) callconv(.C) void { + invoke(activity, "onNativeWindowDestroyed", .{window}); + } + fn onInputQueueCreated(activity: *android.ANativeActivity, input_queue: *android.AInputQueue) callconv(.C) void { + invoke(activity, "onInputQueueCreated", .{input_queue}); + } + fn onInputQueueDestroyed(activity: *android.ANativeActivity, input_queue: *android.AInputQueue) callconv(.C) void { + invoke(activity, "onInputQueueDestroyed", .{input_queue}); + } + fn onContentRectChanged(activity: *android.ANativeActivity, rect: *const android.ARect) callconv(.C) void { + invoke(activity, "onContentRectChanged", .{rect}); + } + }; + return android.ANativeActivityCallbacks{ + .onStart = T.onStart, + .onResume = T.onResume, + .onSaveInstanceState = T.onSaveInstanceState, + .onPause = T.onPause, + .onStop = T.onStop, + .onDestroy = T.onDestroy, + .onWindowFocusChanged = T.onWindowFocusChanged, + .onNativeWindowCreated = T.onNativeWindowCreated, + .onNativeWindowResized = T.onNativeWindowResized, + .onNativeWindowRedrawNeeded = T.onNativeWindowRedrawNeeded, + .onNativeWindowDestroyed = T.onNativeWindowDestroyed, + .onInputQueueCreated = T.onInputQueueCreated, + .onInputQueueDestroyed = T.onInputQueueDestroyed, + .onContentRectChanged = T.onContentRectChanged, + .onConfigurationChanged = T.onConfigurationChanged, + .onLowMemory = T.onLowMemory, + }; +} + +inline fn printSymbolInfoAt(st_index: usize, maybe_debug_info: ?*std.debug.DebugInfo, int_addr: usize) void { + var symbol_name_buffer: [1024]u8 = undefined; + var symbol_name: ?[]const u8 = null; + + if (maybe_debug_info) |di| { + if (di.getModuleForAddress(int_addr)) |module| { + var symbol_buffer: [1024]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&symbol_buffer); + + if (module.getSymbolAtAddress(fba.allocator(), int_addr)) |symbol| { + + // symbol_name_buffer + + symbol_name = std.fmt.bufPrint( + &symbol_name_buffer, + "{s} {s} {s}", + .{ + symbol.symbol_name, + symbol.compile_unit_name, + fmtMaybeLineInfo(symbol.line_info), + }, + ) catch symbol_name; + } else |_| {} + } else |_| {} + } + + std.log.info("#{d:0>2}: 0x{X:0>8} {?s}", .{ + st_index, + int_addr, + symbol_name, + }); +} + +fn realFmtMaybeLineInfo(self: ?std.debug.LineInfo, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + _ = fmt; + _ = options; + if (self) |li| { + try writer.print("{s}:{d}:{d}", .{ + li.file_name, + li.line, + li.column, + }); + } else { + try writer.writeAll(""); + } +} + +fn fmtMaybeLineInfo(li: ?std.debug.LineInfo) std.fmt.Formatter(realFmtMaybeLineInfo) { + return std.fmt.Formatter(realFmtMaybeLineInfo){ + .data = li, + }; +} diff --git a/android/src/audio.zig b/android/src/audio.zig new file mode 100644 index 00000000..89eae5f0 --- /dev/null +++ b/android/src/audio.zig @@ -0,0 +1,94 @@ +const std = @import("std"); +const build_options = @import("build_options"); + +const android = @import("c.zig"); + +const audio_log = std.log.scoped(.audio); + +const Dummy = @import("dummy.zig").Dummy; + +const OpenSL = if (build_options.enable_opensl) @import("opensl.zig").OpenSL else Dummy; +const AAudio = if (build_options.enable_aaudio) @import("aaudio.zig").AAudio else Dummy; + +pub fn midiToFreq(note: usize) f64 { + return std.math.pow(f64, 2, (@intToFloat(f64, note) - 49) / 12) * 440; +} + +pub fn amplitudeTodB(amplitude: f64) f64 { + return 20.0 * std.math.log10(amplitude); +} + +pub fn dBToAmplitude(dB: f64) f64 { + return std.math.pow(f64, 10.0, dB / 20.0); +} + +pub const StreamLayout = struct { + sample_rate: u32, + channel_count: usize, + buffer: union(enum) { + Uint8: []u8, + Int16: []i16, + Float32: []f32, + }, +}; + +const StreamCallbackFn = *const fn (StreamLayout, *anyopaque) void; + +pub const AudioManager = struct {}; + +pub const OutputStreamConfig = struct { + // Leave null to use the the platforms native sampling rate + sample_rate: ?u32 = null, + sample_format: enum { + Uint8, + Int16, + Float32, + }, + buffer_size: ?usize = null, + buffer_count: usize = 4, + channel_count: usize = 1, + callback: StreamCallbackFn, + user_data: *anyopaque, +}; + +pub fn init() !void { + if (build_options.enable_opensl) { + try OpenSL.init(); + } +} + +pub fn getOutputStream(allocator: std.mem.Allocator, config: OutputStreamConfig) !OutputStream { + if (build_options.enable_aaudio) { + return .{ .AAudio = try AAudio.getOutputStream(allocator, config) }; + } + if (build_options.enable_opensl) { + return .{ .OpenSL = try OpenSL.getOutputStream(allocator, config) }; + } + return error.NoBackendsAvailable; +} + +pub const OutputStream = union(enum) { + OpenSL: *OpenSL.OutputStream, + AAudio: *AAudio.OutputStream, + + pub fn stop(output_stream: @This()) void { + switch (output_stream) { + .OpenSL => |opensl| opensl.stop(), + .AAudio => |aaudio| aaudio.stop(), + } + } + + pub fn deinit(output_stream: @This()) void { + switch (output_stream) { + .OpenSL => |opensl| opensl.deinit(), + .AAudio => |aaudio| aaudio.deinit(), + } + } + + pub fn start(output_stream: @This()) !void { + switch (output_stream) { + .OpenSL => |opensl| try opensl.start(), + .AAudio => |aaudio| try aaudio.start(), + } + } +}; diff --git a/android/src/c.zig b/android/src/c.zig new file mode 100644 index 00000000..bb4fd982 --- /dev/null +++ b/android/src/c.zig @@ -0,0 +1,16 @@ +const build_options = @import("build_options"); +pub usingnamespace @cImport({ + @cInclude("EGL/egl.h"); + // @cInclude("EGL/eglext.h"); + @cInclude("GLES2/gl2.h"); + @cInclude("GLES2/gl2ext.h"); + // @cInclude("unwind.h"); + // @cInclude("dlfcn.h"); + if (build_options.enable_aaudio) { + @cInclude("aaudio/AAudio.h"); + } + if (build_options.enable_opensl) { + @cInclude("SLES/OpenSLES.h"); + @cInclude("SLES/OpenSLES_Android.h"); + } +}); diff --git a/android/src/dummy.zig b/android/src/dummy.zig new file mode 100644 index 00000000..e8bc8376 --- /dev/null +++ b/android/src/dummy.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +const OutputStreamConfig = @import("audio.zig").OutputStreamConfig; +const StreamLayout = @import("audio.zig").StreamLayout; + +pub const Dummy = struct { + pub fn getOutputStream(allocator: std.mem.Allocator, config: OutputStreamConfig ) !OutputStream { + _ = allocator; + _ = config; + return error.Unimplemented; + } + + pub const OutputStream = struct { + pub fn stop(_: *@This()) void {} + pub fn deinit(_: *@This()) void {} + pub fn start(_: *@This()) !void{} + }; +}; diff --git a/android/src/egl.zig b/android/src/egl.zig new file mode 100644 index 00000000..678108be --- /dev/null +++ b/android/src/egl.zig @@ -0,0 +1,138 @@ +const std = @import("std"); +const log = std.log.scoped(.egl); + +pub const c = @import("c.zig"); + +const android = @import("android-support.zig"); + +pub const Version = enum { + gles2, + gles3, +}; + +pub const EGLContext = struct { + const Self = @This(); + + display: c.EGLDisplay, + surface: c.EGLSurface, + context: c.EGLContext, + + pub fn init(window: *android.ANativeWindow, version: Version) !Self { + const EGLint = c.EGLint; + + var egl_display = c.eglGetDisplay(null); + if (egl_display == null) { + log.err("Error: No display found!\n", .{}); + return error.FailedToInitializeEGL; + } + + var egl_major: EGLint = undefined; + var egl_minor: EGLint = undefined; + if (c.eglInitialize(egl_display, &egl_major, &egl_minor) == 0) { + log.err("Error: eglInitialise failed!\n", .{}); + return error.FailedToInitializeEGL; + } + + log.info( + \\EGL Version: {s} + \\EGL Vendor: {s} + \\EGL Extensions: {s} + \\ + , .{ + std.mem.span(c.eglQueryString(egl_display, c.EGL_VERSION)), + std.mem.span(c.eglQueryString(egl_display, c.EGL_VENDOR)), + std.mem.span(c.eglQueryString(egl_display, c.EGL_EXTENSIONS)), + }); + + const config_attribute_list = [_]EGLint{ + c.EGL_RED_SIZE, + 8, + c.EGL_GREEN_SIZE, + 8, + c.EGL_BLUE_SIZE, + 8, + c.EGL_ALPHA_SIZE, + 8, + c.EGL_BUFFER_SIZE, + 32, + c.EGL_STENCIL_SIZE, + 0, + c.EGL_DEPTH_SIZE, + 16, + // c.EGL_SAMPLES, 1, + c.EGL_RENDERABLE_TYPE, + switch (version) { + .gles3 => c.EGL_OPENGL_ES3_BIT, + .gles2 => c.EGL_OPENGL_ES2_BIT, + }, + c.EGL_NONE, + }; + + var config: c.EGLConfig = undefined; + var num_config: c.EGLint = undefined; + if (c.eglChooseConfig(egl_display, &config_attribute_list, &config, 1, &num_config) == c.EGL_FALSE) { + log.err("Error: eglChooseConfig failed: 0x{X:0>4}\n", .{c.eglGetError()}); + return error.FailedToInitializeEGL; + } + + log.info("Config: {}\n", .{num_config}); + + const context_attribute_list = [_]EGLint{ c.EGL_CONTEXT_CLIENT_VERSION, 2, c.EGL_NONE }; + + const context = c.eglCreateContext(egl_display, config, null, &context_attribute_list) orelse { + log.err("Error: eglCreateContext failed: 0x{X:0>4}\n", .{c.eglGetError()}); + return error.FailedToInitializeEGL; + }; + errdefer _ = c.eglDestroyContext(egl_display, context); + + log.info("Context created: {?}\n", .{context}); + + var native_window: c.EGLNativeWindowType = @ptrCast(c.EGLNativeWindowType, window); // this is safe, just a C import problem + + const android_width = android.ANativeWindow_getWidth(window); + const android_height = android.ANativeWindow_getHeight(window); + + log.info("Screen Resolution: {}x{}\n", .{ android_width, android_height }); + + const window_attribute_list = [_]EGLint{c.EGL_NONE}; + const egl_surface = c.eglCreateWindowSurface(egl_display, config, native_window, &window_attribute_list) orelse { + log.err("Error: eglCreateWindowSurface failed: 0x{X:0>4}\n", .{c.eglGetError()}); + return error.FailedToInitializeEGL; + }; + errdefer _ = c.eglDestroySurface(egl_display, context); + + log.info("Got Surface: {}\n", .{egl_surface}); + + return Self{ + .display = egl_display, + .surface = egl_surface, + .context = context, + }; + } + + pub fn deinit(self: *Self) void { + _ = c.eglDestroySurface(self.display, self.surface); + _ = c.eglDestroyContext(self.display, self.context); + self.* = undefined; + } + + pub fn swapBuffers(self: Self) !void { + if (c.eglSwapBuffers(self.display, self.surface) == c.EGL_FALSE) { + log.err("Error: eglMakeCurrent failed: 0x{X:0>4}\n", .{c.eglGetError()}); + return error.EglFailure; + } + } + + pub fn makeCurrent(self: Self) !void { + if (c.eglMakeCurrent(self.display, self.surface, self.surface, self.context) == c.EGL_FALSE) { + log.err("Error: eglMakeCurrent failed: 0x{X:0>4}\n", .{c.eglGetError()}); + return error.EglFailure; + } + } + + pub fn release(self: Self) void { + if (c.eglMakeCurrent(self.display, self.surface, self.surface, null) == c.EGL_FALSE) { + log.err("Error: eglMakeCurrent failed: 0x{X:0>4}\n", .{c.eglGetError()}); + } + } +}; diff --git a/android/src/jni.zig b/android/src/jni.zig new file mode 100644 index 00000000..dd63fe40 --- /dev/null +++ b/android/src/jni.zig @@ -0,0 +1,241 @@ +const std = @import("std"); +const log = std.log.scoped(.jni); +const android = @import("android-support.zig"); + +pub const JNI = struct { + const Self = @This(); + + activity: *android.ANativeActivity, + env: *android.JNIEnv, + activity_class: android.jclass, + + pub fn init(activity: *android.ANativeActivity) Self { + var env: *android.JNIEnv = undefined; + _ = activity.vm.*.AttachCurrentThread(activity.vm, &env, null); + + var activityClass = env.*.FindClass(env, "android/app/NativeActivity"); + + return Self{ + .activity = activity, + .env = env, + .activity_class = activityClass, + }; + } + + pub fn deinit(self: *Self) void { + _ = self.activity.vm.*.DetachCurrentThread(self.activity.vm); + self.* = undefined; + } + + fn JniReturnType(comptime function: @TypeOf(.literal)) type { + @setEvalBranchQuota(10_000); + return @typeInfo(@typeInfo(std.meta.fieldInfo(android.JNINativeInterface, function).field_type).Pointer.child).Fn.return_type.?; + } + + fn invokeJni(self: *Self, comptime function: @TypeOf(.literal), args: anytype) JniReturnType(function) { + return @call( + .{}, + @field(self.env.*, @tagName(function)), + .{self.env} ++ args, + ); + } + + fn findClass(self: *Self, class: [:0]const u8) android.jclass { + return self.invokeJni(.FindClass, .{class.ptr}); + } + + pub fn AndroidGetUnicodeChar(self: *Self, keyCode: c_int, metaState: c_int) u21 { + // https://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-c/43871301 + const eventType = android.AKEY_EVENT_ACTION_DOWN; + + const class_key_event = self.findClass("android/view/KeyEvent"); + + const method_get_unicode_char = self.invokeJni(.GetMethodID, .{ class_key_event, "getUnicodeChar", "(I)I" }); + const eventConstructor = self.invokeJni(.GetMethodID, .{ class_key_event, "", "(II)V" }); + const eventObj = self.invokeJni(.NewObject, .{ class_key_event, eventConstructor, eventType, keyCode }); + + const unicodeKey = self.invokeJni(.CallIntMethod, .{ eventObj, method_get_unicode_char, metaState }); + + return @intCast(u21, unicodeKey); + } + + pub fn AndroidMakeFullscreen(self: *Self) void { + // Partially based on + // https://stackoverflow.com/questions/47507714/how-do-i-enable-full-screen-immersive-mode-for-a-native-activity-ndk-app + + // Get android.app.NativeActivity, then get getWindow method handle, returns + // view.Window type + const activityClass = self.findClass("android/app/NativeActivity"); + const getWindow = self.invokeJni(.GetMethodID, .{ activityClass, "getWindow", "()Landroid/view/Window;" }); + const window = self.invokeJni(.CallObjectMethod, .{ self.activity.clazz, getWindow }); + + // Get android.view.Window class, then get getDecorView method handle, returns + // view.View type + const windowClass = self.findClass("android/view/Window"); + const getDecorView = self.invokeJni(.GetMethodID, .{ windowClass, "getDecorView", "()Landroid/view/View;" }); + const decorView = self.invokeJni(.CallObjectMethod, .{ window, getDecorView }); + + // Get the flag values associated with systemuivisibility + const viewClass = self.findClass("android/view/View"); + const flagLayoutHideNavigation = self.invokeJni(.GetStaticIntField, .{ viewClass, self.invokeJni(.GetStaticFieldID, .{ viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION", "I" }) }); + const flagLayoutFullscreen = self.invokeJni(.GetStaticIntField, .{ viewClass, self.invokeJni(.GetStaticFieldID, .{ viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN", "I" }) }); + const flagLowProfile = self.invokeJni(.GetStaticIntField, .{ viewClass, self.invokeJni(.GetStaticFieldID, .{ viewClass, "SYSTEM_UI_FLAG_LOW_PROFILE", "I" }) }); + const flagHideNavigation = self.invokeJni(.GetStaticIntField, .{ viewClass, self.invokeJni(.GetStaticFieldID, .{ viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I" }) }); + const flagFullscreen = self.invokeJni(.GetStaticIntField, .{ viewClass, self.invokeJni(.GetStaticFieldID, .{ viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I" }) }); + const flagImmersiveSticky = self.invokeJni(.GetStaticIntField, .{ viewClass, self.invokeJni(.GetStaticFieldID, .{ viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I" }) }); + + const setSystemUiVisibility = self.invokeJni(.GetMethodID, .{ viewClass, "setSystemUiVisibility", "(I)V" }); + + // Call the decorView.setSystemUiVisibility(FLAGS) + self.invokeJni(.CallVoidMethod, .{ + decorView, + setSystemUiVisibility, + (flagLayoutHideNavigation | flagLayoutFullscreen | flagLowProfile | flagHideNavigation | flagFullscreen | flagImmersiveSticky), + }); + + // now set some more flags associated with layoutmanager -- note the $ in the + // class path search for api-versions.xml + // https://android.googlesource.com/platform/development/+/refs/tags/android-9.0.0_r48/sdk/api-versions.xml + + const layoutManagerClass = self.findClass("android/view/WindowManager$LayoutParams"); + const flag_WinMan_Fullscreen = self.invokeJni(.GetStaticIntField, .{ layoutManagerClass, self.invokeJni(.GetStaticFieldID, .{ layoutManagerClass, "FLAG_FULLSCREEN", "I" }) }); + const flag_WinMan_KeepScreenOn = self.invokeJni(.GetStaticIntField, .{ layoutManagerClass, self.invokeJni(.GetStaticFieldID, .{ layoutManagerClass, "FLAG_KEEP_SCREEN_ON", "I" }) }); + const flag_WinMan_hw_acc = self.invokeJni(.GetStaticIntField, .{ layoutManagerClass, self.invokeJni(.GetStaticFieldID, .{ layoutManagerClass, "FLAG_HARDWARE_ACCELERATED", "I" }) }); + // const int flag_WinMan_flag_not_fullscreen = + // env.GetStaticIntField(layoutManagerClass, + // (env.GetStaticFieldID(layoutManagerClass, "FLAG_FORCE_NOT_FULLSCREEN", + // "I") )); + // call window.addFlags(FLAGS) + self.invokeJni(.CallVoidMethod, .{ + window, + self.invokeJni(.GetMethodID, .{ windowClass, "addFlags", "(I)V" }), + (flag_WinMan_Fullscreen | flag_WinMan_KeepScreenOn | flag_WinMan_hw_acc), + }); + } + + pub fn AndroidDisplayKeyboard(self: *Self, show: bool) bool { + // Based on + // https://stackoverflow.com/questions/5864790/how-to-show-the-soft-keyboard-on-native-activity + var lFlags: android.jint = 0; + + // Retrieves Context.INPUT_METHOD_SERVICE. + const ClassContext = self.findClass("android/content/Context"); + const FieldINPUT_METHOD_SERVICE = self.invokeJni(.GetStaticFieldID, .{ ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;" }); + const INPUT_METHOD_SERVICE = self.invokeJni(.GetStaticObjectField, .{ ClassContext, FieldINPUT_METHOD_SERVICE }); + + // Runs getSystemService(Context.INPUT_METHOD_SERVICE). + const ClassInputMethodManager = self.findClass("android/view/inputmethod/InputMethodManager"); + const MethodGetSystemService = self.invokeJni(.GetMethodID, .{ self.activity_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;" }); + const lInputMethodManager = self.invokeJni(.CallObjectMethod, .{ self.activity.clazz, MethodGetSystemService, INPUT_METHOD_SERVICE }); + + // Runs getWindow().getDecorView(). + const MethodGetWindow = self.invokeJni(.GetMethodID, .{ self.activity_class, "getWindow", "()Landroid/view/Window;" }); + const lWindow = self.invokeJni(.CallObjectMethod, .{ self.activity.clazz, MethodGetWindow }); + const ClassWindow = self.findClass("android/view/Window"); + const MethodGetDecorView = self.invokeJni(.GetMethodID, .{ ClassWindow, "getDecorView", "()Landroid/view/View;" }); + const lDecorView = self.invokeJni(.CallObjectMethod, .{ lWindow, MethodGetDecorView }); + + if (show) { + // Runs lInputMethodManager.showSoftInput(...). + const MethodShowSoftInput = self.invokeJni(.GetMethodID, .{ ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z" }); + return 0 != self.invokeJni(.CallBooleanMethod, .{ lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags }); + } else { + // Runs lWindow.getViewToken() + const ClassView = self.findClass("android/view/View"); + const MethodGetWindowToken = self.invokeJni(.GetMethodID, .{ ClassView, "getWindowToken", "()Landroid/os/IBinder;" }); + const lBinder = self.invokeJni(.CallObjectMethod, .{ lDecorView, MethodGetWindowToken }); + + // lInputMethodManager.hideSoftInput(...). + const MethodHideSoftInput = self.invokeJni(.GetMethodID, .{ ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z" }); + return 0 != self.invokeJni(.CallBooleanMethod, .{ lInputMethodManager, MethodHideSoftInput, lBinder, lFlags }); + } + } + + /// Move the task containing this activity to the back of the activity stack. + /// The activity's order within the task is unchanged. + /// nonRoot: If false then this only works if the activity is the root of a task; if true it will work for any activity in a task. + /// returns: If the task was moved (or it was already at the back) true is returned, else false. + pub fn AndroidSendToBack(self: *Self, nonRoot: bool) bool { + const ClassActivity = self.findClass("android/app/Activity"); + const MethodmoveTaskToBack = self.invokeJni(.GetMethodID, .{ ClassActivity, "moveTaskToBack", "(Z)Z" }); + + return 0 != self.invokeJni(.CallBooleanMethod, .{ self.activity.clazz, MethodmoveTaskToBack, if (nonRoot) @as(c_int, 1) else 0 }); + } + + pub fn AndroidHasPermissions(self: *Self, perm_name: [:0]const u8) bool { + if (android.sdk_version < 23) { + log.err( + "Android SDK version {} does not support AndroidRequestAppPermissions\n", + .{android.sdk_version}, + ); + return false; + } + + const ls_PERM = self.invokeJni(.NewStringUTF, .{perm_name}); + + const PERMISSION_GRANTED = blk: { + var ClassPackageManager = self.findClass("android/content/pm/PackageManager"); + var lid_PERMISSION_GRANTED = self.invokeJni(.GetStaticFieldID, .{ ClassPackageManager, "PERMISSION_GRANTED", "I" }); + break :blk self.invokeJni(.GetStaticIntField, .{ ClassPackageManager, lid_PERMISSION_GRANTED }); + }; + + const ClassContext = self.findClass("android/content/Context"); + const MethodcheckSelfPermission = self.invokeJni(.GetMethodID, .{ ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I" }); + const int_result = self.invokeJni(.CallIntMethod, .{ self.activity.clazz, MethodcheckSelfPermission, ls_PERM }); + return (int_result == PERMISSION_GRANTED); + } + + pub fn AndroidRequestAppPermissions(self: *Self, perm_name: [:0]const u8) void { + if (android.sdk_version < 23) { + log.err( + "Android SDK version {} does not support AndroidRequestAppPermissions\n", + .{android.sdk_version}, + ); + return; + } + + const perm_array = self.invokeJni(.NewObjectArray, .{ + 1, + self.findClass("java/lang/String"), + self.invokeJni(.NewStringUTF, .{perm_name}), + }); + + const MethodrequestPermissions = self.invokeJni(.GetMethodID, .{ self.activity_class, "requestPermissions", "([Ljava/lang/String;I)V" }); + + // Last arg (0) is just for the callback (that I do not use) + self.invokeJni(.CallVoidMethod, .{ self.activity.clazz, MethodrequestPermissions, perm_array, @as(c_int, 0) }); + } + + pub fn getFilesDir(self: *Self, allocator: std.mem.Allocator) ![:0]const u8 { + const getFilesDirMethod = self.invokeJni(.GetMethodID, .{ self.activity_class, "getFilesDir", "()Ljava/io/File;" }); + + const files_dir = self.env.*.CallObjectMethod(self.env, self.activity.clazz, getFilesDirMethod); + + const fileClass = self.findClass("java/io/File"); + + const getPathMethod = self.invokeJni(.GetMethodID, .{ fileClass, "getPath", "()Ljava/lang/String;" }); + + const path_string = self.env.*.CallObjectMethod(self.env, files_dir, getPathMethod); + + const utf8_or_null = self.invokeJni(.GetStringUTFChars, .{ path_string, null }); + + if (utf8_or_null) |utf8_ptr| { + defer self.invokeJni(.ReleaseStringUTFChars, .{ path_string, utf8_ptr }); + + const utf8 = std.mem.sliceTo(utf8_ptr, 0); + + return try allocator.dupeZ(u8, utf8); + } else { + return error.OutOfMemory; + } + } + + comptime { + _ = AndroidGetUnicodeChar; + _ = AndroidMakeFullscreen; + _ = AndroidDisplayKeyboard; + _ = AndroidSendToBack; + _ = AndroidHasPermissions; + _ = AndroidRequestAppPermissions; + } +}; diff --git a/android/src/opensl.zig b/android/src/opensl.zig new file mode 100644 index 00000000..1192bedd --- /dev/null +++ b/android/src/opensl.zig @@ -0,0 +1,496 @@ +const std = @import("std"); + +const c = @import("c.zig"); + +const OutputStreamConfig = @import("audio.zig").OutputStreamConfig; +const StreamLayout = @import("audio.zig").StreamLayout; + +const audio_log = std.log.scoped(.audio); + +// OpenSLES support +pub const OpenSL = struct { + // Global variables + var init_sl_counter: usize = 0; + var engine_object: c.SLObjectItf = undefined; + var engine: c.SLEngineItf = undefined; + + var output_mix: c.SLObjectItf = undefined; + var output_mix_itf: c.SLOutputMixItf = undefined; + + pub const OutputStream = struct { + config: OutputStreamConfig, + player: c.SLObjectItf, + play_itf: c.SLPlayItf, + buffer_queue_itf: c.SLAndroidSimpleBufferQueueItf, + state: c.SLAndroidSimpleBufferQueueState, + + audio_sink: c.SLDataSink, + locator_outputmix: c.SLDataLocator_OutputMix, + + audio_source: c.SLDataSource, + buffer_queue: c.SLDataLocator_BufferQueue, + pcm: c.SLDataFormat_PCM, + + buffer: []i16, + buffer_index: usize, + mutex: std.Thread.Mutex, + allocator: std.mem.Allocator, + + // Must be initialized using OpenSL.getOutputStream + + pub fn stop(output_stream: *OutputStream) void { + checkResult(output_stream.play_itf.*.*.SetPlayState.?(output_stream.play_itf, c.SL_PLAYSTATE_STOPPED)) catch |e| { + audio_log.err("Error stopping stream {s}", .{@errorName(e)}); + }; + } + + pub fn deinit(output_stream: *OutputStream) void { + output_stream.player.*.*.Destroy.?(output_stream.player); + output_stream.allocator.free(output_stream.buffer); + } + + pub fn start(output_stream: *OutputStream) !void { + // Get player interface + try checkResult(output_stream.player.*.*.GetInterface.?( + output_stream.player, + c.SL_IID_PLAY, + @ptrCast(*anyopaque, &output_stream.play_itf), + )); + + // Get buffer queue interface + try checkResult(output_stream.player.*.*.GetInterface.?( + output_stream.player, + c.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + @ptrCast(*anyopaque, &output_stream.buffer_queue_itf), + )); + + // Register callback + try checkResult(output_stream.buffer_queue_itf.*.*.RegisterCallback.?( + output_stream.buffer_queue_itf, + bufferQueueCallback, + @ptrCast(*anyopaque, output_stream), + )); + + // Enqueue a few buffers to get the ball rollng + var i: usize = 0; + while (i < output_stream.config.buffer_count) : (i += 1) { + try checkResult(output_stream.buffer_queue_itf.*.*.Enqueue.?( + output_stream.buffer_queue_itf, + &output_stream.buffer[output_stream.buffer_index], + @intCast(u32, output_stream.config.buffer_size.? * (output_stream.pcm.containerSize / 8)), + )); + output_stream.buffer_index += output_stream.config.buffer_size.?; + } + + output_stream.buffer_index = (output_stream.buffer_index + output_stream.config.buffer_size.?) % output_stream.buffer.len; + + // Start playing queued audio buffers + try checkResult(output_stream.play_itf.*.*.SetPlayState.?(output_stream.play_itf, c.SL_PLAYSTATE_PLAYING)); + + audio_log.info("started opensl output stream", .{}); + } + }; + + pub fn bufferQueueCallback(queue_itf: c.SLAndroidSimpleBufferQueueItf, user_data: ?*anyopaque) callconv(.C) void { + var output_stream = @ptrCast(*OutputStream, @alignCast(@alignOf(OutputStream), user_data)); + + // Lock the mutex to prevent race conditions + output_stream.mutex.lock(); + defer output_stream.mutex.unlock(); + + var buffer = output_stream.buffer[output_stream.buffer_index .. output_stream.buffer_index + output_stream.config.buffer_size.?]; + + for (buffer) |*frame| { + frame.* = 0; + } + + var stream_layout = StreamLayout{ + .sample_rate = output_stream.pcm.samplesPerSec / 1000, + .channel_count = output_stream.pcm.numChannels, + .buffer = .{ .Int16 = buffer }, + }; + + output_stream.config.callback(stream_layout, output_stream.config.user_data); + + checkResult(queue_itf.*.*.Enqueue.?( + queue_itf, + @ptrCast(*anyopaque, buffer.ptr), + @intCast(c.SLuint32, (output_stream.pcm.containerSize / 8) * buffer.len), + )) catch |e| { + audio_log.err("Error enqueueing buffer! {s}", .{@errorName(e)}); + }; + + output_stream.buffer_index = (output_stream.buffer_index + output_stream.config.buffer_size.?) % output_stream.buffer.len; + } + + pub fn init() SLError!void { + init_sl_counter += 1; + if (init_sl_counter == 1) { + try printInterfaces(); + errdefer init_sl_counter -= 1; // decrement counter on failure + + // Get engine object + try checkResult(c.slCreateEngine(&engine_object, 0, null, 0, null, null)); + + // Initialize engine object + try checkResult(engine_object.*.*.Realize.?(engine_object, c.SL_BOOLEAN_FALSE)); + errdefer engine_object.*.*.Destroy.?(engine_object); + + // Get engine interface + try checkResult(engine_object.*.*.GetInterface.?(engine_object, c.SL_IID_ENGINE, @ptrCast(*anyopaque, &engine))); + try printEngineInterfaces(); + try printEngineExtensions(); + + // Get OutputMix object + try checkResult(engine.*.*.CreateOutputMix.?(engine, &output_mix, 0, null, null)); + try checkResult(output_mix.*.*.Realize.?(output_mix, c.SL_BOOLEAN_FALSE)); + errdefer output_mix.*.*.Destroy.?(output_mix); + + // Get OutputMix interface + try checkResult(output_mix.*.*.GetInterface.?(output_mix, c.SL_IID_OUTPUTMIX, @ptrCast(*anyopaque, &output_mix_itf))); + } + } + + pub fn deinit() void { + std.debug.assert(init_sl_counter > 0); + + // spinlock lock + { + init_sl_counter -= 1; + if (init_sl_counter == 0) { + output_mix.*.*.Destroy.?(output_mix); + engine_object.*.*.Destroy.?(engine_object); + } + } + // spinlock unlock + } + + pub fn getOutputStream(allocator: std.mem.Allocator, conf: OutputStreamConfig) !*OutputStream { + // TODO: support multiple formats + std.debug.assert(conf.sample_format == .Int16); + + var config = conf; + config.buffer_size = config.buffer_size orelse 256; + config.sample_rate = config.sample_rate orelse 44100; + + // Allocate memory for audio buffer + // TODO: support other formats + var buffers = try allocator.alloc(i16, config.buffer_size.? * config.buffer_count); + errdefer allocator.free(buffers); + + for (buffers) |*sample| { + sample.* = 0; + } + + // Initialize the context for Buffer queue callbacks + var output_stream = try allocator.create(OutputStream); + output_stream.* = OutputStream{ + // We don't have these values yet + .player = undefined, + .play_itf = undefined, + .state = undefined, + .buffer_queue_itf = undefined, + + // Store user defined callback information + .config = config, + + // Store pointer to audio buffer + .buffer = buffers, + .buffer_index = 0, + + // Setup the format of the content in the buffer queue + .buffer_queue = .{ + .locatorType = c.SL_DATALOCATOR_BUFFERQUEUE, + .numBuffers = @intCast(u32, config.buffer_count), + }, + .pcm = .{ + .formatType = c.SL_DATAFORMAT_PCM, + .numChannels = @intCast(u32, config.channel_count), + .samplesPerSec = config.sample_rate.? * 1000, // OpenSL ES uses milliHz instead of Hz, for some reason + .bitsPerSample = switch (config.sample_format) { + .Uint8 => c.SL_PCMSAMPLEFORMAT_FIXED_8, + .Int16 => c.SL_PCMSAMPLEFORMAT_FIXED_16, + .Float32 => c.SL_PCMSAMPLEFORMAT_FIXED_32, + }, + .containerSize = switch (config.sample_format) { + .Uint8 => 8, + .Int16 => 16, + .Float32 => 32, + }, + .channelMask = c.SL_SPEAKER_FRONT_CENTER, // TODO + .endianness = c.SL_BYTEORDER_LITTLEENDIAN, // TODO + + }, + + // Configure audio source + .audio_source = .{ + .pFormat = @ptrCast(*anyopaque, &output_stream.pcm), + .pLocator = @ptrCast(*anyopaque, &output_stream.buffer_queue), + }, + .locator_outputmix = .{ + .locatorType = c.SL_DATALOCATOR_OUTPUTMIX, + .outputMix = output_mix, + }, + // Configure audio output + .audio_sink = .{ + .pLocator = @ptrCast(*anyopaque, &output_stream.locator_outputmix), + .pFormat = null, + }, + + // Thread safety + .mutex = std.Thread.Mutex{}, + + .allocator = allocator, + }; + + // Create the music player + try checkResult(engine.*.*.CreateAudioPlayer.?( + engine, + &output_stream.player, + &output_stream.audio_source, + &output_stream.audio_sink, + 1, + &[_]c.SLInterfaceID{c.SL_IID_BUFFERQUEUE}, + &[_]c.SLboolean{c.SL_BOOLEAN_TRUE}, + )); + + // Realize the player interface + try checkResult(output_stream.player.*.*.Realize.?(output_stream.player, c.SL_BOOLEAN_FALSE)); + + // Return to user for them to start + return output_stream; + } + + const Result = enum(u32) { + Success = c.SL_RESULT_SUCCESS, + PreconditionsViolated = c.SL_RESULT_PRECONDITIONS_VIOLATED, + ParameterInvalid = c.SL_RESULT_PARAMETER_INVALID, + MemoryFailure = c.SL_RESULT_MEMORY_FAILURE, + ResourceError = c.SL_RESULT_RESOURCE_ERROR, + ResourceLost = c.SL_RESULT_RESOURCE_LOST, + IoError = c.SL_RESULT_IO_ERROR, + BufferInsufficient = c.SL_RESULT_BUFFER_INSUFFICIENT, + ContentCorrupted = c.SL_RESULT_CONTENT_CORRUPTED, + ContentUnsupported = c.SL_RESULT_CONTENT_UNSUPPORTED, + ContentNotFound = c.SL_RESULT_CONTENT_NOT_FOUND, + PermissionDenied = c.SL_RESULT_PERMISSION_DENIED, + FeatureUnsupported = c.SL_RESULT_FEATURE_UNSUPPORTED, + InternalError = c.SL_RESULT_INTERNAL_ERROR, + UnknownError = c.SL_RESULT_UNKNOWN_ERROR, + OperationAborted = c.SL_RESULT_OPERATION_ABORTED, + ControlLost = c.SL_RESULT_CONTROL_LOST, + _, + }; + + const SLError = error{ + PreconditionsViolated, + ParameterInvalid, + MemoryFailure, + ResourceError, + ResourceLost, + IoError, + BufferInsufficient, + ContentCorrupted, + ContentUnsupported, + ContentNotFound, + PermissionDenied, + FeatureUnsupported, + InternalError, + UnknownError, + OperationAborted, + ControlLost, + }; + + pub fn checkResult(result: u32) SLError!void { + const tag = std.meta.intToEnum(Result, result) catch return error.UnknownError; + return switch (tag) { + .Success => {}, + .PreconditionsViolated => error.PreconditionsViolated, + .ParameterInvalid => error.ParameterInvalid, + .MemoryFailure => error.MemoryFailure, + .ResourceError => error.ResourceError, + .ResourceLost => error.ResourceLost, + .IoError => error.IoError, + .BufferInsufficient => error.BufferInsufficient, + .ContentCorrupted => error.ContentCorrupted, + .ContentUnsupported => error.ContentUnsupported, + .ContentNotFound => error.ContentNotFound, + .PermissionDenied => error.PermissionDenied, + .FeatureUnsupported => error.FeatureUnsupported, + .InternalError => error.InternalError, + .UnknownError => error.UnknownError, + .OperationAborted => error.OperationAborted, + .ControlLost => error.ControlLost, + else => error.UnknownError, + }; + } + + fn printInterfaces() !void { + var interface_count: c.SLuint32 = undefined; + try checkResult(c.slQueryNumSupportedEngineInterfaces(&interface_count)); + { + var i: c.SLuint32 = 0; + while (i < interface_count) : (i += 1) { + var interface_id: c.SLInterfaceID = undefined; + try checkResult(c.slQuerySupportedEngineInterfaces(i, &interface_id)); + const interface_tag = InterfaceID.fromIid(interface_id); + if (interface_tag) |tag| { + audio_log.info("OpenSL engine interface id: {s}", .{@tagName(tag)}); + } + } + } + } + + fn printEngineExtensions() !void { + var extension_count: c.SLuint32 = undefined; + try checkResult(engine.*.*.QueryNumSupportedExtensions.?(engine, &extension_count)); + { + var i: c.SLuint32 = 0; + while (i < extension_count) : (i += 1) { + var extension_ptr: [4096]u8 = undefined; + var extension_size: c.SLint16 = 4096; + try checkResult(engine.*.*.QuerySupportedExtension.?(engine, i, &extension_ptr, &extension_size)); + var extension_name = extension_ptr[0..@intCast(usize, extension_size)]; + audio_log.info("OpenSL engine extension {}: {s}", .{ i, extension_name }); + } + } + } + + fn printEngineInterfaces() !void { + var interface_count: c.SLuint32 = undefined; + try checkResult(engine.*.*.QueryNumSupportedInterfaces.?(engine, c.SL_OBJECTID_ENGINE, &interface_count)); + { + var i: c.SLuint32 = 0; + while (i < interface_count) : (i += 1) { + var interface_id: c.SLInterfaceID = undefined; + try checkResult(engine.*.*.QuerySupportedInterfaces.?(engine, c.SL_OBJECTID_ENGINE, i, &interface_id)); + const interface_tag = InterfaceID.fromIid(interface_id); + if (interface_tag) |tag| { + audio_log.info("OpenSL engine interface id: {s}", .{@tagName(tag)}); + } else { + audio_log.info("Unknown engine interface id: {}", .{interface_id.*}); + } + } + } + } + + fn iidEq(iid1: c.SLInterfaceID, iid2: c.SLInterfaceID) bool { + return iid1.*.time_low == iid2.*.time_low and + iid1.*.time_mid == iid2.*.time_mid and + iid1.*.time_hi_and_version == iid2.*.time_hi_and_version and + iid1.*.clock_seq == iid2.*.clock_seq and + iid1.*.time_mid == iid2.*.time_mid and + std.mem.eql(u8, &iid1.*.node, &iid2.*.node); + } + + const InterfaceID = enum { + AudioIODeviceCapabilities, + Led, + Vibra, + MetadataExtraction, + MetadataTraversal, + DynamicSource, + OutputMix, + Play, + PrefetchStatus, + PlaybackRate, + Seek, + Record, + Equalizer, + Volume, + DeviceVolume, + Object, + BufferQueue, + PresetReverb, + EnvironmentalReverb, + EffectSend, + _3DGrouping, + _3DCommit, + _3DLocation, + _3DDoppler, + _3DSource, + _3DMacroscopic, + MuteSolo, + DynamicInterfaceManagement, + MidiMessage, + MidiTempo, + MidiMuteSolo, + MidiTime, + AudioDecoderCapabilities, + AudioEncoder, + AudioEncoderCapabilities, + BassBoost, + Pitch, + RatePitch, + Virtualizer, + Visualization, + Engine, + EngineCapabilities, + ThreadSync, + AndroidEffect, + AndroidEffectSend, + AndroidEffectCapabilities, + AndroidConfiguration, + AndroidSimpleBufferQueue, + AndroidBufferQueueSource, + AndroidAcousticEchoCancellation, + AndroidAutomaticGainControl, + AndroidNoiseSuppresssion, + fn fromIid(iid: c.SLInterfaceID) ?InterfaceID { + if (iidEq(iid, c.SL_IID_NULL)) return null; + if (iidEq(iid, c.SL_IID_AUDIOIODEVICECAPABILITIES)) return .AudioIODeviceCapabilities; + if (iidEq(iid, c.SL_IID_LED)) return .Led; + if (iidEq(iid, c.SL_IID_VIBRA)) return .Vibra; + if (iidEq(iid, c.SL_IID_METADATAEXTRACTION)) return .MetadataExtraction; + if (iidEq(iid, c.SL_IID_METADATATRAVERSAL)) return .MetadataTraversal; + if (iidEq(iid, c.SL_IID_DYNAMICSOURCE)) return .DynamicSource; + if (iidEq(iid, c.SL_IID_OUTPUTMIX)) return .OutputMix; + if (iidEq(iid, c.SL_IID_PLAY)) return .Play; + if (iidEq(iid, c.SL_IID_PREFETCHSTATUS)) return .PrefetchStatus; + if (iidEq(iid, c.SL_IID_PLAYBACKRATE)) return .PlaybackRate; + if (iidEq(iid, c.SL_IID_SEEK)) return .Seek; + if (iidEq(iid, c.SL_IID_RECORD)) return .Record; + if (iidEq(iid, c.SL_IID_EQUALIZER)) return .Equalizer; + if (iidEq(iid, c.SL_IID_VOLUME)) return .Volume; + if (iidEq(iid, c.SL_IID_DEVICEVOLUME)) return .DeviceVolume; + if (iidEq(iid, c.SL_IID_OBJECT)) return .Object; + if (iidEq(iid, c.SL_IID_BUFFERQUEUE)) return .BufferQueue; + if (iidEq(iid, c.SL_IID_PRESETREVERB)) return .PresetReverb; + if (iidEq(iid, c.SL_IID_ENVIRONMENTALREVERB)) return .EnvironmentalReverb; + if (iidEq(iid, c.SL_IID_EFFECTSEND)) return .EffectSend; + if (iidEq(iid, c.SL_IID_3DGROUPING)) return ._3DGrouping; + if (iidEq(iid, c.SL_IID_3DCOMMIT)) return ._3DCommit; + if (iidEq(iid, c.SL_IID_3DLOCATION)) return ._3DLocation; + if (iidEq(iid, c.SL_IID_3DDOPPLER)) return ._3DDoppler; + if (iidEq(iid, c.SL_IID_3DSOURCE)) return ._3DSource; + if (iidEq(iid, c.SL_IID_3DMACROSCOPIC)) return ._3DMacroscopic; + if (iidEq(iid, c.SL_IID_MUTESOLO)) return .MuteSolo; + if (iidEq(iid, c.SL_IID_DYNAMICINTERFACEMANAGEMENT)) return .DynamicInterfaceManagement; + if (iidEq(iid, c.SL_IID_MIDIMESSAGE)) return .MidiMessage; + if (iidEq(iid, c.SL_IID_MIDITEMPO)) return .MidiTempo; + if (iidEq(iid, c.SL_IID_MIDIMUTESOLO)) return .MidiMuteSolo; + if (iidEq(iid, c.SL_IID_MIDITIME)) return .MidiTime; + if (iidEq(iid, c.SL_IID_AUDIODECODERCAPABILITIES)) return .AudioDecoderCapabilities; + if (iidEq(iid, c.SL_IID_AUDIOENCODER)) return .AudioEncoder; + if (iidEq(iid, c.SL_IID_AUDIOENCODERCAPABILITIES)) return .AudioEncoderCapabilities; + if (iidEq(iid, c.SL_IID_BASSBOOST)) return .BassBoost; + if (iidEq(iid, c.SL_IID_PITCH)) return .Pitch; + if (iidEq(iid, c.SL_IID_RATEPITCH)) return .RatePitch; + if (iidEq(iid, c.SL_IID_VIRTUALIZER)) return .Virtualizer; + if (iidEq(iid, c.SL_IID_VISUALIZATION)) return .Visualization; + if (iidEq(iid, c.SL_IID_ENGINE)) return .Engine; + if (iidEq(iid, c.SL_IID_ENGINECAPABILITIES)) return .EngineCapabilities; + if (iidEq(iid, c.SL_IID_THREADSYNC)) return .ThreadSync; + if (iidEq(iid, c.SL_IID_ANDROIDEFFECT)) return .AndroidEffect; + if (iidEq(iid, c.SL_IID_ANDROIDEFFECTSEND)) return .AndroidEffectSend; + if (iidEq(iid, c.SL_IID_ANDROIDEFFECTCAPABILITIES)) return .AndroidEffectCapabilities; + if (iidEq(iid, c.SL_IID_ANDROIDCONFIGURATION)) return .AndroidConfiguration; + if (iidEq(iid, c.SL_IID_ANDROIDSIMPLEBUFFERQUEUE)) return .AndroidSimpleBufferQueue; + if (iidEq(iid, c.SL_IID_ANDROIDBUFFERQUEUESOURCE)) return .AndroidBufferQueueSource; + if (iidEq(iid, c.SL_IID_ANDROIDACOUSTICECHOCANCELLATION)) return .AndroidAcousticEchoCancellation; + if (iidEq(iid, c.SL_IID_ANDROIDAUTOMATICGAINCONTROL)) return .AndroidAutomaticGainControl; + if (iidEq(iid, c.SL_IID_ANDROIDNOISESUPPRESSION)) return .AndroidNoiseSuppresssion; + return null; + } + }; +}; diff --git a/android/tools/zip_add.zig b/android/tools/zip_add.zig new file mode 100644 index 00000000..f4179a7b --- /dev/null +++ b/android/tools/zip_add.zig @@ -0,0 +1,46 @@ +const std = @import("std"); + +const c = @cImport({ + @cInclude("zip.h"); +}); + +const Error = error{ + FailedToWriteEntry, + FileNotFound, + FailedToCreateEntry, + Overflow, + OutOfMemory, + InvalidCmdLine, +}; + +// zip_add file.zip local_path zip_path +pub fn main() Error!u8 { + const allocator = std.heap.c_allocator; + + const args = try std.process.argsAlloc(allocator); + if (args.len != 4) + return 1; + + const zip_file = args[1]; + const src_file_name = args[2]; + const dst_file_name = args[3]; + + errdefer |e| switch (@as(Error, e)) { + error.FailedToWriteEntry => std.log.err("could not find {s}", .{src_file_name}), + error.FileNotFound => std.log.err("could not open {s}", .{zip_file}), + error.FailedToCreateEntry => std.log.err("could not create {s}", .{dst_file_name}), + else => {}, + }; + + const zip = c.zip_open(zip_file.ptr, c.ZIP_DEFAULT_COMPRESSION_LEVEL, 'a') orelse return error.FileNotFound; + defer c.zip_close(zip); + + if (c.zip_entry_open(zip, dst_file_name.ptr) < 0) + return error.FailedToCreateEntry; + defer _ = c.zip_entry_close(zip); + + if (c.zip_entry_fwrite(zip, src_file_name.ptr) < 0) + return error.FailedToWriteEntry; + + return 0; +} diff --git a/android/vendor/kuba-zip/miniz.h b/android/vendor/kuba-zip/miniz.h new file mode 100644 index 00000000..3bbe2d93 --- /dev/null +++ b/android/vendor/kuba-zip/miniz.h @@ -0,0 +1,6875 @@ +/* + miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP + reading/writing/appending, PNG writing See "unlicense" statement at the end + of this file. Rich Geldreich , last updated Oct. 13, + 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: + http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the + archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of + all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Change History + 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major + release with Zip64 support (almost there!): + - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug + (thanks kahmyong.moon@hp.com) which could cause locate files to not find + files. This bug would only have occured in earlier versions if you explicitly + used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or + mz_zip_add_mem_to_archive_file_in_place() (which used this flag). If you + can't switch to v1.15 but want to fix this bug, just remove the uses of this + flag from both helper funcs (and of course don't use the flag). + - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when + pUser_read_buf is not NULL and compressed size is > uncompressed size + - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract + compressed data from directory entries, to account for weird zipfiles which + contain zero-size compressed data on dir entries. Hopefully this fix won't + cause any issues on weird zip archives, because it assumes the low 16-bits of + zip external attributes are DOS attributes (which I believe they always are + in practice). + - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the + internal attributes, just the filename and external attributes + - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed + - Added cmake support for Linux builds which builds all the examples, + tested with clang v3.3 and gcc v4.6. + - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti + - Merged MZ_FORCEINLINE fix from hdeanclark + - Fix include before config #ifdef, thanks emil.brink + - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping + (super useful for OpenGL apps), and explicit control over the compression + level (so you can set it to 1 for real-time compression). + - Merged in some compiler fixes from paulharris's github repro. + - Retested this build under Windows (VS 2010, including static analysis), + tcc 0.9.26, gcc v4.6 and clang v3.3. + - Added example6.c, which dumps an image of the mandelbrot set to a PNG + file. + - Modified example2 to help test the + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. + - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix + possible src file fclose() leak if alignment bytes+local header file write + faiiled + - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the + wrong central dir header offset, appears harmless in this release, but it + became a problem in the zip64 branch 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 + compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). + 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix + mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. + - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and + re-ran a randomized regression test on ~500k files. + - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. + - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze + (static analysis) option and fixed all warnings (except for the silly "Use of + the comma-operator in a tested expression.." analysis warning, which I + purposely use to work around a MSVC compiler warning). + - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and + tested Linux executables. The codeblocks workspace is compatible with + Linux+Win32/x64. + - Added miniz_tester solution/project, which is a useful little app + derived from LZHAM's tester app that I use as part of the regression test. + - Ran miniz.c and tinfl.c through another series of regression testing on + ~500,000 files and archives. + - Modified example5.c so it purposely disables a bunch of high-level + functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the + MINIZ_NO_STDIO bug report.) + - Fix ftell() usage in examples so they exit with an error on files which + are too large (a limitation of the examples, not miniz itself). 4/12/12 v1.12 + - More comments, added low-level example5.c, fixed a couple minor + level_and_flags issues in the archive API's. level_and_flags can now be set + to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson + for the feedback/bug report. 5/28/11 v1.11 - Added statement from + unlicense.org 5/27/11 v1.10 - Substantial compressor optimizations: + - Level 1 is now ~4x faster than before. The L1 compressor's throughput + now varies between 70-110MB/sec. on a + - Core i7 (actual throughput varies depending on the type of data, and x64 + vs. x86). + - Improved baseline L2-L9 compression perf. Also, greatly improved + compression perf. issues on some file types. + - Refactored the compression code for better readability and + maintainability. + - Added level 10 compression level (L10 has slightly better ratio than + level 9, but could have a potentially large drop in throughput on some + files). 5/15/11 v1.09 - Initial stable release. + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, + and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, + and Huffman-only streams. It performs and compresses approximately as well as + zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is + implemented as a single function coroutine: see tinfl_decompress(). It + supports decompression into a 32KB (or larger power of 2) wrapping buffer, or + into a memory block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory + allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough + functionality present for it to be a drop-in zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly + routines. Supports raw deflate streams or standard zlib streams with adler-32 + checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or + zlib static dictionaries. I've tried to closely emulate zlib's various + flavors of stream flushing and return status codes, but there are no + guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, + originally written by Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in + mind, with just enough abstraction to get the job done with minimal fuss. + There are simple API's to retrieve file information, read files from existing + archives, create new archives, append new files to existing archives, or + clone archive data from one archive to another. It supports archives located + in memory or the heap, on disk (using stdio.h), or you can specify custom + file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a + disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const + char *pArchive_name, size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an + archive, the entire central directory is located and read as-is into memory, + and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a + loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, + const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one + example) can be used to identify multiple versions of the same file in an + archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using + mz_zip_reader_get_num_files()) and retrieve detailed info on each file by + calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer + immediately writes compressed file data to disk and builds an exact image of + the central directory in memory. The central directory image is written all + at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file + data to any power of 2 alignment, which can be useful when the archive will + be read from optical media. Also, the writer supports placing arbitrary data + blobs at the very beginning of ZIP archives. Archives written using either + feature are still readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is + to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, + const char *pArchive_name, const void *pBuf, size_t buf_size, const void + *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be + appended to. Note the appending is done in-place and is not an atomic + operation, so if something goes wrong during the operation it's possible the + archive could be left without a central directory (although the local file + headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, + cloning only those bits you want to preserve into a new archive using using + the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and + rename the newly written archive, and you're done. This is safe but requires + a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using + mz_zip_writer_init_from_reader(), append new files as needed, then finalize + the archive which will write an updated central directory to the original + archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() + does.) There's a possibility that the archive's central directory could be + lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle + unencrypted, stored or deflated files. Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, + either cut and paste the below header, or create miniz.h, #define + MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your + target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define + MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before + including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), + stat64(), etc. Otherwise you won't be able to process large files (i.e. + 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include +#include + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be +// CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on +// stdio for file I/O. +//#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able +// to get the current time, or get/set file times, and the C run-time funcs that +// get/set times won't be called. The current downside is the times written to +// your archives will be from 1979. +//#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive +// API's. +//#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression +// API's. +//#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent +// conflicts against stock zlib. +//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom +// user alloc/free/realloc callbacks to the zlib and archive API's, and a few +// stand-alone helper API's which don't provide custom user functions (such as +// tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +//#define MINIZ_NO_MALLOC + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +// TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc +// on Linux +#define MINIZ_NO_TIME +#endif + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ + defined(__i386) || defined(__i486__) || defined(__i486) || \ + defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient + * integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || \ + defined(_LP64) || defined(__LP64__) || defined(__ia64__) || \ + defined(__x86_64__) +// 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). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __APPLE__ +#define ftello64 ftello +#define fseeko64 fseeko +#define fopen64 fopen +#define freopen64 freopen + +// Darwin OSX +#define MZ_PLATFORM 19 +#endif + +#ifndef MZ_PLATFORM +#if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__) +#define MZ_PLATFORM 0 +#else +// UNIX +#define MZ_PLATFORM 3 +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some +// parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! +typedef unsigned long mz_ulong; + +// mz_free() internally uses the MZ_FREE() macro (which by default calls free() +// unless you've modified the MZ_MALLOC macro) to release a block allocated from +// the heap. +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with +// ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with +// ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or + * modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purposely differ from zlib's: +// items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, + size_t size); + +#define MZ_VERSION "9.1.15" +#define MZ_VERNUM 0x91F0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 15 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The +// other values are for advanced use (refer to the zlib docs). +enum { + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +// 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. +enum { + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s { + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func + zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been +// optimized purely for performance, not ratio. (This special func. is +// currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and +// MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with +// zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no +// header or footer) mem_level must be between [1, 9] (it's checked but +// ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, + int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as +// calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input +// and producing as much output as possible. Parameters: +// pStream is the stream to read from and write to. You must initialize/update +// the next_in, avail_in, next_out, and avail_out members. flush may be +// MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// MZ_OK on success (when flushing, or if more input is needed but not +// available, and/or there's more output to be written but the output buffer +// is full). MZ_STREAM_END if all input has been consumed and all output bytes +// have been written. Don't call mz_deflate() on the stream anymore. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input and/or +// output buffers are empty. (Fill up the input buffer or free up some output +// space and try again.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// mz_deflateBound() returns a (very) conservative upper bound on the amount of +// data that could be generated by deflate(), assuming flush is set to only +// MZ_NO_FLUSH or MZ_FINISH. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on +// failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of +// data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// mz_inflateInit2() is like mz_inflateInit() with an additional option that +// controls the window size and whether or not the stream has been wrapped with +// a zlib header/footer: window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse +// zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// Decompresses the input stream to the output, consuming only as much of the +// input as needed, and writing as much to the output as possible. Parameters: +// pStream is the stream to read from and write to. You must initialize/update +// the next_in, avail_in, next_out, and avail_out members. flush may be +// MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. On the first call, if flush is +// MZ_FINISH it's assumed the input and output buffers are both sized large +// enough to decompress the entire stream in a single call (this is slightly +// faster). MZ_FINISH implies that there are no more source bytes available +// beside what's already in the input buffer, and that the output buffer is +// large enough to hold the rest of the decompressed data. +// Return values: +// MZ_OK on success. Either more input is needed but not available, and/or +// there's more output to be written but the output buffer is full. +// MZ_STREAM_END if all needed input has been consumed and all output bytes +// have been written. For zlib streams, the adler-32 of the decompressed data +// has also been verified. MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input buffer is +// empty but the inflater needs more input to continue, or if the output +// buffer is not large enough. Call mz_inflate() again with more input data, +// or with more room in the output buffer (except when using single call +// decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on +// failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the +// error code is invalid. +const char *mz_error(int err); + +// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used +// as a drop-in replacement for the subset of zlib that miniz.c supports. Define +// MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib +// in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// An attempt to work around MSVC's spammy "warning C4127: conditional +// expression is constant" message. +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum { + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct { + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, + void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, + const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +typedef struct { + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, + mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, + size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, + mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, + mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, + mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, + mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// Returns the number of bytes written to pFilename, or if filename_buf_size is +// 0 this function returns the number of bytes needed to fully store the +// filename. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, + char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, + const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, + mz_uint file_index, void *pBuf, + size_t buf_size, mz_uint flags, + void *pUser_read_buf, + size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( + mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, + mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, + void *pBuf, size_t buf_size, + mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, + const char *pFilename, void *pBuf, + size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, + size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, + const char *pFilename, size_t *pSize, + mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, + mz_uint file_index, + mz_file_write_func pCallback, + void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, + const char *pFilename, + mz_file_write_func pCallback, + void *pOpaque, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and +// modified times. This function only extracts files, not archive directory +// records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, + const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, + const char *pArchive_filename, + const char *pDst_filename, + mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive +// file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, + size_t size_to_reserve_at_beginning, + size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, + mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient +// in-place file appends to occur on an existing archive. For archives opened +// using mz_zip_reader_init_file, pFilename must be the archive's filename so it +// can be reopened for writing. If the file can't be reopened, +// mz_zip_reader_end() will be called. For archives opened using +// mz_zip_reader_init_mem, the memory block must be growable using the realloc +// callback (which defaults to realloc unless you've overridden it). Finally, +// for archives opened using mz_zip_reader_init, the mz_zip_archive's user +// provided m_pWrite function cannot be NULL. Note: In-place archive +// modification is not recommended unless you know what you're doing, because if +// execution stops or something goes wrong before the archive is finalized the +// file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, + const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record +// the current local time into the archive. To add a directory entry, call this +// method with an archive name ending in a forwardslash with empty buffer. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, +// MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or +// just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, + const void *pBuf, size_t buf_size, + mz_uint level_and_flags); +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, + const char *pArchive_name, const void *pBuf, + size_t buf_size, const void *pComment, + mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, + mz_uint32 uncomp_crc32); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records +// the disk file's modified time into the archive. level_and_flags - compression +// level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd +// with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, + const char *pSrc_filename, const void *pComment, + mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint32 ext_attributes); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no +// recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, + mz_zip_archive *pSource_zip, + mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by +// the end of central directory record. After an archive is finalized, the only +// valid call on the mz_zip_archive struct is mz_zip_writer_end(). An archive +// must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, + size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if +// mz_zip_writer_init_file() was used. Note for the archive to be valid, it must +// have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) +// appends a memory blob to a ZIP archive. level_and_flags - compression level +// (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero +// or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_add_mem_to_archive_file_in_place( + const char *pZip_filename, const char *pArchive_name, const void *pBuf, + size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, + const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags used by tinfl_decompress(). +// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and +// ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the +// input is a raw deflate stream. TINFL_FLAG_HAS_MORE_INPUT: If set, there are +// more input bytes available beyond the end of the supplied input buffer. If +// clear, the input buffer contains all remaining input. +// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large +// enough to hold the entire decompressed stream. If clear, the output buffer is +// at least the size of the dictionary (typically 32KB). +// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the +// decompressed bytes. +enum { + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block +// allocated via malloc(). On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data +// to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger +// than src_buf_len on uncompressible data. The caller must call mz_free() on +// the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, + size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block +// in memory. Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the +// number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags); + +// tinfl_decompress_mem_to_callback() decompresses a block in memory to an +// internal 32KB buffer, and a user provided callback function will be called to +// flush the buffer. Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, + tinfl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum { + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) \ + do { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// Main low-level decompressor coroutine function. This is the only function +// actually needed for decompression. All the other functions are just +// high-level helpers for improved usability. This is a universal API, i.e. it +// can be used as a building block to build any desired higher level +// decompression API. In the limit case, it can be called once per every byte +// input or output. +tinfl_status tinfl_decompress(tinfl_decompressor *r, + const mz_uint8 *pIn_buf_next, + size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, + mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, + const mz_uint32 decomp_flags); + +// Internal/private bits follow. +enum { + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct { + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], + m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag { + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, + m_check_adler32, m_dist, m_counter, m_num_extra, + m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], + m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly +// slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 0 + +// tdefl_init() compression flags logically OR'd together (low 12 bits contain +// the max. number of probes per dictionary search): 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). +enum { + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +// 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. TDEFL_COMPUTE_ADLER32: Always compute the +// adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more +// efficient lazy parsing. 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). TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a +// distance of 1) TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. +// The low 12 bits are reserved to control the max # of hash probes per +// dictionary lookup (see TDEFL_MAX_PROBES_MASK). +enum { + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block +// allocated via malloc(). On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against +// the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger +// than src_buf_len on uncompressible data. The caller must free() the returned +// block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, + size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in +// memory. Returns 0 on failure. +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be +// 1, 2, 3, or 4. The image pitch in bytes per scanline will be w*num_chans. +// The leftmost pixel on the top scanline is stored first in memory. level may +// range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, +// MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL If flip is +// true, the image will be flipped on the Y axis (useful for OpenGL apps). +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must mz_free() the returned heap block (which will typically be +// larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, + int h, int num_chans, + size_t *pLen_out, + mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, + int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write +// compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, + void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The +// above helpers use this function internally. +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, + tdefl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags); + +enum { + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed +// output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum { + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +// 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. +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +// tdefl's compression state structure. +typedef struct { + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, + m_bit_buffer; + 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; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +// There is no corresponding deinit() function because the tdefl API's do not +// dynamically allocate memory. pBut_buf_func: If NULL, output data will be +// supplied to the specified callback. In this case, the user should call the +// tdefl_compress_buffer() API for compression. If pBut_buf_func is NULL the +// user should always call the tdefl_compress() API. flags: See the above enums +// (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) +tdefl_status tdefl_init(tdefl_compressor *d, + tdefl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags); + +// 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. +tdefl_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); + +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a +// non-NULL tdefl_put_buf_func_ptr. tdefl_compress_buffer() always consumes the +// entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, + size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't +// defined, because it uses some of its macros. +#ifndef MINIZ_NO_ZLIB_APIS +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be +// much slower on some files) window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, +// MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, + int strategy); +#endif // #ifndef MINIZ_NO_ZLIB_APIS + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + +// ------------------- End of Header: Implementation follows. (If you only want +// the header, define MINIZ_HEADER_FILE_ONLY.) + +#ifndef MINIZ_HEADER_FILE_ONLY + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#include +#include + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) \ + ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ + ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) \ + ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ + ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | \ + ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | \ + ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) \ + (((mz_uint64)MZ_READ_LE32(p)) | \ + (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) \ + << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE inline __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API's + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C +// implementation that balances processor cache usage against speed": +// http://www.geocities.com/malbrain/ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { + static const mz_uint32 s_crc32[16] = { + 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, + 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c}; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; +} + +void mz_free(void *p) { MZ_FREE(p); } + +#ifndef MINIZ_NO_ZLIB_APIS + +static void *def_alloc_func(void *opaque, size_t items, size_t size) { + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +static void def_free_func(void *opaque, void *address) { + (void)opaque, (void)address; + MZ_FREE(address); +} +static void *def_realloc_func(void *opaque, void *address, size_t items, + size_t size) { + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) { return MZ_VERSION; } + +int mz_deflateInit(mz_streamp pStream, int level) { + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, + MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, + int mem_level, int strategy) { + tdefl_compressor *pComp; + mz_uint comp_flags = + TDEFL_COMPUTE_ADLER32 | + tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || + ((window_bits != MZ_DEFAULT_WINDOW_BITS) && + (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = def_alloc_func; + if (!pStream->zfree) + pStream->zfree = def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, + sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) { + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || + (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, + ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) { + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || + (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == + TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, + pStream->next_in, &in_bytes, pStream->next_out, + &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) { + mz_status = MZ_STREAM_ERROR; + break; + } else if (defl_status == TDEFL_STATUS_DONE) { + mz_status = MZ_STREAM_END; + break; + } else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { + if ((flush) || (pStream->total_in != orig_total_in) || + (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; // Can't make forward progress without some input. + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) { + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { + (void)pStream; + // This is really over conservative. (And lame, but it's actually pretty + // tricky to compute a true upper bound given the way tdefl's blocking works.) + return MZ_MAX(128 + (source_len * 110) / 100, + 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len, int level) { + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len) { + return mz_compress2(pDest, pDest_len, pSource, source_len, + MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) { + return mz_deflateBound(NULL, source_len); +} + +typedef struct { + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) { + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && + (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = def_alloc_func; + if (!pStream->zfree) + pStream->zfree = def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, + sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) { + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) { + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) { + // MZ_FINISH on the first call implies that the input and output buffers are + // large enough to hold the entire compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, + pStream->next_out, pStream->next_out, &out_bytes, + decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && + (!pState->m_dict_avail)) + ? MZ_STREAM_END + : MZ_OK; + } + + for (;;) { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress( + &pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, + pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some + // uncompressed data left in the output dictionary - + // oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress + // without supplying more input or by setting flush + // to MZ_FINISH. + else if (flush == MZ_FINISH) { + // The output buffer MUST be large to hold the remaining uncompressed data + // when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's + // at least 1 more byte on the way. If there's no more room left in the + // output buffer then something is wrong. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || + (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) + ? MZ_STREAM_END + : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) { + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len) { + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR + : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) { + static struct { + int m_err; + const char *m_pDesc; + } s_error_descs[] = {{MZ_OK, ""}, + {MZ_STREAM_END, "stream end"}, + {MZ_NEED_DICT, "need dictionary"}, + {MZ_ERRNO, "file error"}, + {MZ_STREAM_ERROR, "stream error"}, + {MZ_DATA_ERROR, "data error"}, + {MZ_MEM_ERROR, "out of memory"}, + {MZ_BUF_ERROR, "buf error"}, + {MZ_VERSION_ERROR, "version error"}, + {MZ_PARAM_ERROR, "parameter error"}}; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Low-level Decompression (completely independent from all +// compression API's) + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do { \ + for (;;) { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +// TODO: If the caller has indicated that there's no more input, and we attempt +// to read beyond the input buf, then something is wrong with the input because +// the inflator never reads ahead more than it needs to. Currently +// TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. +#define TINFL_GET_BYTE(state_index, c) \ + do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for (;;) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do { \ + if (num_bits < (mz_uint)(n)) { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do { \ + if (num_bits < (mz_uint)(n)) { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes +// remaining in the input buffer falls below 2. It reads just enough bytes from +// the input stream that are needed to decode the next Huffman code (and +// absolutely no more). It works by trying to fully decode a Huffman code by +// using whatever bits are currently present in the bit buffer. If this fails, +// it reads another byte, and tries again until it succeeds or until the bit +// buffer contains >=15 bits (deflate's max. Huffman code size). +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex +// than you would initially expect because the zlib API expects the decompressor +// to never read beyond the final byte of the deflate stream. (In other words, +// when this macro wants to read another byte from the input, it REALLY needs +// another byte in order to fully decode the next Huffman code.) Handling this +// properly is particularly important on raw deflate (non-zlib) streams, which +// aren't followed by a byte aligned adler-32. The slow path is only executed at +// the very end of the input buffer. +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | \ + (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= \ + 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, + const mz_uint8 *pIn_buf_next, + size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, + mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, + const mz_uint32 decomp_flags) { + static const int s_length_base[31] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const int s_length_extra[31] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, + 4, 4, 5, 5, 5, 5, 0, 0, 0}; + static const int s_dist_base[32] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, + 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, + 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; + static const int s_dist_extra[32] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + static const mz_uint8 s_length_dezigzag[19] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + static const int s_min_table_sizes[3] = {257, 1, 4}; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = + pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = + pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = + (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) + ? (size_t)-1 + : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, + dist_from_out_buf_start; + + // Ensure the output buffer's size is a power of 2, unless the output buffer + // is large enough to hold the entire output file (in which case it doesn't + // matter). + if (((out_buf_size_mask + 1) & out_buf_size_mask) || + (pOut_buf_next < pOut_buf_start)) { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || + (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || + ((out_buf_size_mask + 1) < + (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != + (mz_uint)(0xFFFF ^ + (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } else { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), + (size_t)(pIn_buf_end - pIn_buf_cur)), + counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } else if (r->m_type == 3) { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } else { + if (r->m_type == 1) { + mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } else { + for (counter = 0; counter < 3; counter++) { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) { + int tree_next, tree_cur; + tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], + total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; + sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { + mz_uint rev_code = 0, l, cur_code, + code_size = pTable->m_code_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == + (tree_cur = pTable->m_look_up[rev_code & + (TINFL_FAST_LOOKUP_SIZE - 1)])) { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = + (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + rev_code >>= 1; + tree_cur -= (rev_code & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) { + for (counter = 0; + counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, + (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, + r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, + r->m_len_codes + r->m_table_sizes[0], + r->m_table_sizes[1]); + } + } + for (;;) { + mz_uint8 *pSrc; + for (;;) { + if (((pIn_buf_end - pIn_buf_cur) < 4) || + ((pOut_buf_end - pOut_buf_cur) < 2)) { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } else { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) { + bit_buf |= + (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) { + bit_buf |= + (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = + r->m_tables[0] + .m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= + 0) + code_len = sym2 >> 9; + else { + code_len = TINFL_FAST_LOOKUP_BITS; + do { + sym2 = r->m_tables[0] + .m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) { + bit_buf |= + (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = + r->m_tables[0] + .m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= + 0) + code_len = sym2 >> 9; + else { + code_len = TINFL_FAST_LOOKUP_BITS; + do { + sym2 = r->m_tables[0] + .m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && + (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { + while (counter--) { + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = + pOut_buf_start[(dist_from_out_buf_start++ - dist) & + out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) { + if (counter) { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { + TINFL_SKIP_BITS(32, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf; + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & + (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && + (status >= 0)) { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, + s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && + (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && + (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +// Higher level helper functions. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, + size_t *pOut_len, int flags) { + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) { + size_t src_buf_size = src_buf_len - src_buf_ofs, + dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress( + &decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, + (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, + &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags) { + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = + tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, + (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED + : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, + tinfl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags) { + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, + dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = + tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, + &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && + (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +// ------------------- Low-level Compression (independent from all decompression +// API's) + +// Purposely making these tables static for faster init and thread safety. +static const mz_uint16 s_tdefl_len_sym[256] = { + 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, 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, 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, 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, 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, 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, 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, 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}; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 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, 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, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 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}; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 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, 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, + 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, 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, 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, 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}; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 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, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 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, 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, 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, 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, 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, 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, 7, 7, 7, 7, 7, 7, 7, 7}; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 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, 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, 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}; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 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, 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, 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}; + +// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted +// values. +typedef struct { + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, + tdefl_sym_freq *pSyms0, + tdefl_sym_freq *pSyms1) { + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = + pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, +// alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) { + if (leaf >= n || A[root].m_key < A[leaf].m_key) { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) { + while (root >= 0 && (int)A[root].m_key == dpth) { + used++; + root--; + } + while (avbl > used) { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +// Limits canonical Huffman code table's max code size. +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, + int code_list_len, + int max_code_size) { + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, + int table_len, int code_size_limit, + int static_table) { + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } else { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], + *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, + code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)( \ + d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = \ + (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) { \ + if (rle_z_count < 3) { \ + d->m_huff_count[2][0] = \ + (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } else if (rle_z_count <= 10) { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = \ + (mz_uint8)(rle_z_count - 3); \ + } else { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = \ + (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static 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}; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) { + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, + rle_repeat_count, packed_code_sizes_index; + 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; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], + sizeof(mz_uint8) * num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], + sizeof(mz_uint8) * num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, + sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } else { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = + (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } else if (++rle_repeat_count == 6) { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { + TDEFL_RLE_PREV_CODE_SIZE(); + } else { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes + [2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS( + d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; + packed_code_sizes_index < num_packed_code_sizes;) { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], + "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) { + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && \ + MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; + flags >>= 1) { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], + match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + 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]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], + s_tdefl_len_extra[match_len]); + + // This sequence coaxes MSVC into using cmov's vs. jmp's. + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], + d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], + num_extra_bits); + } else { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], + d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], + d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], + d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; + flags >>= 1) { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], + match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + 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]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], + s_tdefl_len_extra[match_len]); + + if (match_dist < 512) { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } else { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } else { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && + // MINIZ_HAS_64BIT_REGISTERS + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) { + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + 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; + mz_uint8 *pOutput_buf_start = + ((d->m_pPut_buf_func == NULL) && + ((*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; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = + tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || + (d->m_total_lz_bytes < 48)); + + // If the block gets expanded, forget the current contents of the output + // buffer and send a raw block instead. + if (((use_raw_block) || + ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= + d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) { + TDEFL_PUT_BITS( + d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], + 8); + } + } + // Check for the extremely unlikely (if not impossible) case of the compressed + // block not fitting into the output buffer when using dynamic codes. + else if (!comp_block_succeeded) { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) { + if (flush == TDEFL_FINISH) { + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } else { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, + sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, + sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { + if (d->m_pPut_buf_func) { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } else if (pOutput_buf_start == d->m_output_buf) { + int bytes_to_copy = (int)MZ_MIN( + (size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, + bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } else { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#define TDEFL_READ_UNALIGNED_WORD(p) ((p)[0] | (p)[1] << 8) +static 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) { + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, + match_len = *pMatch_len, probe_pos = pos, next_probe_pos, + probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), + s01 = *s; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) { + for (;;) { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || \ + ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (*q != s01) + continue; + p = s; + probe_len = 32; + do { + } while ((*(++p) == *(++q)) && (*(++p) == *(++q)) && (*(++p) == *(++q)) && + (*(++p) == *(++q)) && (--probe_len > 0)); + if (!probe_len) { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); + break; + } else if ((probe_len = ((mz_uint)(p - s) * 2) + + (mz_uint)(*(const mz_uint8 *)p == + *(const mz_uint8 *)q)) > match_len) { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == + max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static 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) { + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, + match_len = *pMatch_len, probe_pos = pos, next_probe_pos, + probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) { + for (;;) { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || \ + ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && \ + (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) { + // Faster, minimally featured LZRW1-style match+parse loop with better + // register utilization. Intended for applications where raw throughput is + // valued more highly than ratio. + 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; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = + (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( + d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, + MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = + (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & + TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= + dict_size) && + ((mz_uint32)( + *(d->m_dict + (probe_pos & TDEFL_LZ_DICT_SIZE_MASK)) | + (*(d->m_dict + ((probe_pos & TDEFL_LZ_DICT_SIZE_MASK) + 1)) + << 8) | + (*(d->m_dict + ((probe_pos & TDEFL_LZ_DICT_SIZE_MASK) + 2)) + << 16)) == first_trigram)) { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = + (const mz_uint16 *)(d->m_dict + + (probe_pos & TDEFL_LZ_DICT_SIZE_MASK)); + mz_uint32 probe_len = 32; + do { + } while ((*(++p) == *(++q)) && (*(++p) == *(++q)) && + (*(++p) == *(++q)) && (*(++p) == *(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || + ((cur_match_len == TDEFL_MIN_MATCH_LEN) && + (cur_match_dist >= 8U * 1024U))) { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } else { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && + (cur_match_dist >= 1) && + (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - + TDEFL_MIN_MATCH_LEN]]++; + } + } else { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, + mz_uint8 lit) { + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void +tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && + (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) { + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + // Update dictionary and hash chains. Keeps the lookahead size equal to + // TDEFL_MAX_MATCH_LEN. + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { + 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; + 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]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( + src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } else { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & + TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + 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); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = + MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + // Simple lazy/greedy parsing state machine. + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = + d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } else { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, + d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + 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))) { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) { + if (cur_match_len > d->m_saved_match_len) { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } else { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } else { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } else if (!cur_match_dist) + tdefl_record_literal(d, + d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || + (cur_match_len >= 128)) { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } else { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + // Move the lookahead forward by len_to_move bytes. + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); + // Check if it's time to flush the current LZ codes to the internal output + // buffer. + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((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)))) { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { + if (d->m_pIn_buf_size) { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, + d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, + d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE + : TDEFL_STATUS_OKAY; +} + +tdefl_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) { + if (!d) { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == + ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || + (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || + (pIn_buf_size && *pIn_buf_size && !pIn_buf) || + (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | + TDEFL_RLE_MATCHES)) == 0)) { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } else +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && + (pIn_buf)) + d->m_adler32 = + (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, + d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && + (!d->m_output_flush_remaining)) { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, + size_t in_buf_size, tdefl_flush flush) { + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, + tdefl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags) { + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + 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; + 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; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, + sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, + sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, + tdefl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags) { + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == + TDEFL_STATUS_OKAY); + succeeded = + succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == + TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct { + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, + void *pUser) { + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, + size_t *pOut_len, int flags) { + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output( + pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags) { + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output( + pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +#ifndef MINIZ_NO_ZLIB_APIS +static const mz_uint s_tdefl_num_probes[11] = {0, 1, 6, 32, 16, 32, + 128, 256, 512, 768, 1500}; + +// 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). +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, + int strategy) { + mz_uint comp_flags = + s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | + ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} +#endif // MINIZ_NO_ZLIB_APIS + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) // nonstandard extension used : non-constant + // aggregate initializer (also supported by GNU + // C and C99, so no big deal) +#endif + +// Simple PNG writer function by Alex Evans, 2011. Released into the public +// domain: https://gist.github.com/908299, more context at +// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. +// This is actually a modification of Alex's original code so PNG files +// generated by this function pass pngcheck. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, + int h, int num_chans, + size_t *pLen_out, + mz_uint level, mz_bool flip) { + // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was + // defined. + static const mz_uint s_tdefl_png_num_probes[11] = { + 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; + tdefl_compressor *pComp = + (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { + MZ_FREE(pComp); + return NULL; + } + // write dummy header + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + // compress image data + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, + s_tdefl_png_num_probes[MZ_MIN(10, level)] | + TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, + (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, + bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != + TDEFL_STATUS_DONE) { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + // write real header + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; + mz_uint8 pnghdr[41] = {0x89, + 0x50, + 0x4e, + 0x47, + 0x0d, + 0x0a, + 0x1a, + 0x0a, + 0x00, + 0x00, + 0x00, + 0x0d, + 0x49, + 0x48, + 0x44, + 0x52, + 0, + 0, + (mz_uint8)(w >> 8), + (mz_uint8)w, + 0, + 0, + (mz_uint8)(h >> 8), + (mz_uint8)h, + 8, + chans[num_chans], + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (mz_uint8)(*pLen_out >> 24), + (mz_uint8)(*pLen_out >> 16), + (mz_uint8)(*pLen_out >> 8), + (mz_uint8)*pLen_out, + 0x49, + 0x44, + 0x41, + 0x54}; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + // write footer (IDAT CRC-32, followed by IEND chunk) + if (!tdefl_output_buffer_putter( + "\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, + *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + // compute final size of file, grab compressed data buffer and return + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, + int num_chans, size_t *pLen_out) { + // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we + // can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's + // where #defined out) + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, + pLen_out, 6, MZ_FALSE); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// ------------------- .ZIP archive reading + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#include + +#if defined(_MSC_VER) || defined(__MINGW32__) + +#include + +static wchar_t *str2wstr(const char *str) { + int len = strlen(str) + 1; + wchar_t *wstr = (wchar_t*)malloc(len * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, str, len * sizeof(char), wstr, len); + return wstr; +} + +static FILE *mz_fopen(const char *pFilename, const char *pMode) { + wchar_t *wFilename = str2wstr(pFilename); + wchar_t *wMode = str2wstr(pMode); + FILE *pFile = _wfopen(wFilename, wMode); + + free(wFilename); + free(wMode); + + return pFile; +} + +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { + wchar_t *wPath = str2wstr(pPath); + wchar_t *wMode = str2wstr(pMode); + FILE *pFile = _wfreopen(wPath, wMode, pStream); + + free(wPath); + free(wMode); + + return pFile; +} + +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +// ZIG_ANDROID_MOD: Zig's mingw64 doesn't have _ftelli64/_fseeki64 +#if defined(__MINGW64__) +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#else +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#endif +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN(f, m) mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__GNUC__) && _LARGEFILE64_SOURCE +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#else +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#else +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif // #ifdef _MSC_VER +#endif // #ifdef MINIZ_NO_STDIO + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +// Various ZIP archive enums. To completely avoid cross platform compiler +// alignment and platform endian issues, miniz.c doesn't use structs for any of +// this stuff. +enum { + // ZIP archive identifiers and record sizes + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + // Central directory header record offsets + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + // Local directory header offsets + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + // End of central directory offsets + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct { + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag { + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. + */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 + * will also be slammed to true too, even if we didn't find a zip64 end of + * central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write + * helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) \ + (array_ptr)->m_element_size = element_size +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ + ((element_type *)((array_ptr)->m_p))[index] + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, + mz_zip_array *pArray) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, + mz_zip_array *pArray, + size_t min_new_capacity, + mz_uint growing) { + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, + pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, + mz_zip_array *pArray, + size_t new_capacity, + mz_uint growing) { + if (new_capacity > pArray->m_capacity) { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, + mz_zip_array *pArray, + size_t new_size, + mz_uint growing) { + if (new_size > pArray->m_capacity) { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, + mz_zip_array *pArray, + size_t n) { + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, + mz_zip_array *pArray, + const void *pElements, + size_t n) { + if (0 == n) + return MZ_TRUE; + if (!pElements) + return MZ_FALSE; + + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, + pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) { + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(time_t time, mz_uint16 *pDOS_time, + mz_uint16 *pDOS_date) { +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, + time_t *pTime) { + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= + * 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, + time_t modified_time) { + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, + mz_zip_error err_num) { + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, + mz_uint32 flags) { + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, + sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, + sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, + sizeof(mz_uint32)); + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool +mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, + const mz_zip_array *pCentral_dir_offsets, + mz_uint l_index, mz_uint r_index) { + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, + l_index)), + *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), + r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +// Heap sort of lowercased filenames, used to help accelerate plain central +// directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), +// but it could allocate memory.) +static void +mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + int start = (size - 2) >> 1, end; + while (start >= 0) { + int child, root = start; + for (;;) { + if ((child = (root << 1) + 1) >= size) + break; + child += + (((child + 1) < size) && + (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, + pIndices[child], pIndices[child + 1]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, + pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + start--; + } + + end = size - 1; + while (end > 0) { + int child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) { + if ((child = (root << 1) + 1) >= end) + break; + child += + (((child + 1) < end) && + mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, + pIndices[child], pIndices[child + 1])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, + pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, + mz_uint32 record_sig, + mz_uint32 record_size, + mz_int64 *pOfs) { + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = + MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) { + int i, + n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" + * (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= + (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, + mz_uint flags) { + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, + cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = + ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32 + [(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32 + [(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = + (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first + * 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig( + pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { + if (pZip->m_pRead(pZip->m_pIO_opaque, + cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, + pZip64_locator, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { + zip64_end_of_central_dir_ofs = MZ_READ_LE64( + pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > + (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, + pZip64_end_of_central_dir, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = + MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) { + mz_uint32 zip64_total_num_of_disks = + MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < + (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = + (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough + * for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && + ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another + * heap block to hold the unsorted central dir file record offsets, and + * possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, + MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, + pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) { + if (!mz_zip_array_resize(pZip, + &pZip->m_pState->m_sorted_central_dir_offsets, + pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, + pZip->m_pState->m_central_dir.m_p, + cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some + * basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { + mz_uint total_header_size, disk_index, bit_flags, filename_size, + ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || + (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, + i) = + (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, + mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == + MZ_UINT32_MAX)) { + /* Attempt to find zip64 extended information field in the entry's extra + * data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) { + const mz_uint8 *pExtra_data; + void *buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > + n) { + buf = MZ_MALLOC(ext_data_size); + if (buf == NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, + cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + filename_size, + buf, ext_data_size) != ext_data_size) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8 *)buf; + } else { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > + extra_size_remaining) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { + /* Ok, the archive didn't have any zip64 headers but it uses a + * zip64 extended information field so mark it as zip64 anyway + * (this can occur with infozip's zip util when it reads + * compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = + extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext + * data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && + (decomp_size != comp_size)) || + (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || + ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > + n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, + mz_uint32 flags) { + if ((!pZip) || (!pZip->m_pRead)) + return MZ_FALSE; + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, + void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) + ? 0 + : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, + size_t size, mz_uint32 flags) { + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + pZip->m_pState->m_mem_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, + void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || + (((cur_ofs != (mz_int64)file_ofs)) && + (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, + mz_uint32 flags) { + mz_uint64 file_size; + MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return MZ_FALSE; + if (MZ_FSEEK64(pFile, 0, SEEK_END)) { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + file_size = MZ_FTELL64(pFile); + if (!mz_zip_reader_init_internal(pZip, flags)) { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { + return pZip ? pZip->m_total_files : 0; +} + +static MZ_FORCEINLINE const mz_uint8 * +mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || + (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, + file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, + mz_uint file_index) { + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & 1); +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, + mz_uint file_index) { + mz_uint filename_len, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + // First see if the filename ends with a '/' character. + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + // Bugfix: This code was also checking if the internal attribute was non-zero, + // which wasn't correct. Most/all zip writers (hopefully) set DOS + // file/directory attributes in the low 16-bits, so check for the DOS + // directory flag and ignore the source OS ID in the created by field. + // FIXME: Remove this check? Is it necessary - we already check the filename. + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & 0x10) != 0) + return MZ_TRUE; + + return MZ_FALSE; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, + mz_zip_archive_file_stat *pStat) { + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if ((!p) || (!pStat)) + return MZ_FALSE; + + // Unpack the central directory record. + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = + mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), + MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + // Copy as much of the filename and comment as possible. + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, + p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), + n); + pStat->m_comment[n] = '\0'; + + return MZ_TRUE; +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, + char *pFilename, mz_uint filename_buf_size) { + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) { + if (filename_buf_size) + pFilename[0] = '\0'; + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, + const char *pB, + mz_uint len, + mz_uint flags) { + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int +mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, + const mz_zip_array *pCentral_dir_offsets, + mz_uint l_index, const char *pR, mz_uint r_len) { + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, + l_index)), + *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, + const char *pFilename) { + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + int l = 0, h = size - 1; + while (l <= h) { + int m = (l + h) >> 1, file_index = pIndices[m], + comp = + mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, + file_index, pFilename, filename_len); + if (!comp) + return file_index; + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + return -1; +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, + const char *pComment, mz_uint flags) { + mz_uint file_index; + size_t name_len, comment_len; + if ((!pZip) || (!pZip->m_pState) || (!pName) || + (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return -1; + if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && + (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + return mz_zip_reader_locate_file_binary_search(pZip, pName); + name_len = strlen(pName); + if (name_len > 0xFFFF) + return -1; + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > 0xFFFF) + return -1; + for (file_index = 0; file_index < pZip->m_total_files; file_index++) { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, + file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = + (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), + file_comment_len = + MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || + (!mz_zip_reader_string_equal(pComment, pFile_comment, + file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { + int ofs = filename_len - 1; + do { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || + (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && + (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) + return file_index; + } + return -1; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, + mz_uint file_index, void *pBuf, + size_t buf_size, mz_uint flags, + void *pUser_read_buf, + size_t user_read_buf_size) { + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, + out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((buf_size) && (!pBuf)) + return MZ_FALSE; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips + // with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have + // compressed deflate data which inflates to 0 bytes, but these entries claim + // to uncompress to 512 bytes in the headers). I'm torn how to handle this + // case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && + (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Ensure supplied output buffer is large enough. + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size + : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, + (size_t)needed_size) != needed_size) + return MZ_FALSE; + return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || + (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, + (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); + } + + // Decompress the file either directly from memory or from a file input + // buffer. + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) { + // Read directly from the archive in memory. + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } else if (pUser_read_buf) { + // Use a user provided read buffer. + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } else { + // Temporarily allocate a read buffer. + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return MZ_FALSE; + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do { + size_t in_buf_size, + out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, + (size_t)read_buf_avail) != read_buf_avail) { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress( + &inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, + (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | + (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || + (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, + (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( + mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, + mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, + flags, pUser_read_buf, + user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, + void *pBuf, size_t buf_size, + mz_uint flags) { + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, + flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, + const char *pFilename, void *pBuf, + size_t buf_size, mz_uint flags) { + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, + buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, + size_t *pSize, mz_uint flags) { + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + if (!p) + return NULL; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + return NULL; + if (NULL == + (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + return NULL; + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, + flags)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, + const char *pFilename, size_t *pSize, + mz_uint flags) { + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, + mz_uint file_index, + mz_file_write_func pCallback, + void *pOpaque, mz_uint flags) { + int status = TINFL_STATUS_DONE; + mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, + out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips + // with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have + // compressed deflate data which inflates to 0 bytes, but these entries claim + // to uncompress to 512 bytes in the headers). I'm torn how to handle this + // case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && + (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + // Decompress the file either directly from memory or from a file input + // buffer. + if (pZip->m_pState->m_pMem) { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } else { + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pState->m_pMem) { + if (((sizeof(size_t) == sizeof(mz_uint32))) && + (file_stat.m_comp_size > 0xFFFFFFFF)) + return MZ_FALSE; + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, + (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + status = TINFL_STATUS_FAILED; + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = + (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, + (size_t)file_stat.m_comp_size); + // cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + // comp_remaining = 0; + } else { + while (comp_remaining) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, + (size_t)read_buf_avail) != read_buf_avail) { + status = TINFL_STATUS_FAILED; + break; + } + + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32( + file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, + (size_t)read_buf_avail) != read_buf_avail) { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } else { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + TINFL_LZ_DICT_SIZE))) + status = TINFL_STATUS_FAILED; + else { + do { + mz_uint8 *pWrite_buf_cur = + (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, + out_buf_size = + TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, + (size_t)read_buf_avail) != read_buf_avail) { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress( + &inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, + (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, + comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != + out_buf_size) { + status = TINFL_STATUS_FAILED; + break; + } + file_crc32 = + (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || + (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && + (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || + (file_crc32 != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, + const char *pFilename, + mz_file_write_func pCallback, + void *pOpaque, mz_uint flags) { + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, + flags); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, + const void *pBuf, size_t n) { + (void)ofs; + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, + const char *pDst_filename, + mz_uint flags) { + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return MZ_FALSE; + status = mz_zip_reader_extract_to_callback( + pZip, file_index, mz_zip_file_write_callback, pFile, flags); + if (MZ_FCLOSE(pFile) == EOF) + return MZ_FALSE; +#ifndef MINIZ_NO_TIME + if (status) { + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); + } +#endif + + return status; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || + (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, + const char *pArchive_filename, + const char *pDst_filename, + mz_uint flags) { + int file_index = + mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} +#endif + +// ------------------- .ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || + (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (pZip->m_file_offset_alignment) { + // Ensure user specified file offset alignment is a power of 2. + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return MZ_FALSE; + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, + sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, + sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, + sizeof(mz_uint32)); + return MZ_TRUE; +} + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, + const void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if ((!n) || + ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) + return 0; + + if (new_size > pState->m_mem_capacity) { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + while (new_capacity < new_size) + new_capacity *= 2; + if (NULL == (pNew_block = pZip->m_pRealloc( + pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + return 0; + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, + size_t size_to_reserve_at_beginning, + size_t initial_allocation_size) { + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, + size_to_reserve_at_beginning))) { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, + const void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || + (((cur_ofs != (mz_int64)file_ofs)) && + (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, + mz_uint64 size_to_reserve_at_beginning) { + MZ_FILE *pFile; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_pFile = pFile; + if (size_to_reserve_at_beginning) { + mz_uint64 cur_ofs = 0; + char buf[4096]; + MZ_CLEAR_OBJ(buf); + do { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, + const char *pFilename) { + mz_zip_internal_state *pState; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + // No sense in trying to write to an archive that's already at the support max + // size + if ((pZip->m_total_files == 0xFFFF) || + ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if (pState->m_pFile) { +#ifdef MINIZ_NO_STDIO + pFilename; + return MZ_FALSE; +#else + // Archive is being read from stdio - try to reopen as writable. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + if (!pFilename) + return MZ_FALSE; + pZip->m_pWrite = mz_zip_file_write_func; + if (NULL == + (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { + // The mz_zip_archive is now in a bogus state because pState->m_pFile is + // NULL, so just close it. + mz_zip_reader_end(pZip); + return MZ_FALSE; + } +#endif // #ifdef MINIZ_NO_STDIO + } else if (pState->m_pMem) { + // Archive lives in a memory block. Assume it's from the heap that we can + // resize using the realloc callback. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + } + // Archive is being read via a user provided read function - make sure the + // user has specified a write function too. + else if (!pZip->m_pWrite) + return MZ_FALSE; + + // Start writing new files at the archive's current central directory + // location. + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_central_directory_file_ofs = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, + const void *pBuf, size_t buf_size, + mz_uint level_and_flags) { + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, + level_and_flags, 0, 0); +} + +typedef struct { + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, + void *pUser) { + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, + pState->m_cur_archive_file_ofs, pBuf, + len) != len) + return MZ_FALSE; + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_local_dir_header( + mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, + mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, + mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, + mz_uint16 dos_time, mz_uint16 dos_date) { + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header( + mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, + mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, + mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, + mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { + (void)pZip; + mz_uint16 version_made_by = 10 * MZ_VER_MAJOR + MZ_VER_MINOR; + version_made_by |= (MZ_PLATFORM << 8); + + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_MADE_BY_OFS, version_made_by); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir( + mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, + mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, + mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, + mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, + mz_uint32 ext_attributes) { + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + // No zip64 support yet + if ((local_header_ofs > 0xFFFFFFFF) || + (((mz_uint64)pState->m_central_dir.m_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + + comment_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_central_dir_header( + pZip, central_dir_header, filename_size, extra_size, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, + dos_date, local_header_ofs, ext_attributes)) + return MZ_FALSE; + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, + filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, + extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, + comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, + ¢ral_dir_ofs, 1))) { + // Try to push the central directory array back into its original state. + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, + MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { + // Basic ZIP archive filename validity checks: Valid filenames cannot start + // with a forward slash, cannot contain a drive letter, and cannot use + // DOS-style backward slashes. + if (*pArchive_name == '/') + return MZ_FALSE; + while (*pArchive_name) { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + pArchive_name++; + } + return MZ_TRUE; +} + +static mz_uint +mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (pZip->m_file_offset_alignment - n) & + (pZip->m_file_offset_alignment - 1); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, + mz_uint64 cur_file_ofs, mz_uint32 n) { + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return MZ_FALSE; + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, + const char *pArchive_name, const void *pBuf, + size_t buf_size, const void *pComment, + mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, + mz_uint32 uncomp_crc32) { + mz_uint32 ext_attributes = 0; + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + store_data_uncompressed = + ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || + (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || + (!pArchive_name) || ((comment_size) && (!pComment)) || + (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; + pState = pZip->m_pState; + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return MZ_FALSE; + // No zip64 support yet + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + +#ifndef MINIZ_NO_TIME + { + time_t cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif // #ifndef MINIZ_NO_TIME + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || + ((pZip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { + // Set DOS Subdirectory attribute bit. + ext_attributes |= 0x10; + // Subdirectories cannot contain data. + if ((buf_size) || (uncomp_size)) + return MZ_FALSE; + } + + // Try to do any allocations before writing to the archive, so if an + // allocation fails the file remains unmodified. (A good idea if we're doing + // an in-place modification.) + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size)) || + (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return MZ_FALSE; + + if ((!store_data_uncompressed) && (buf_size)) { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return MZ_FALSE; + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, + num_alignment_padding_bytes + + sizeof(local_dir_header))) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == + 0); + } + cur_archive_file_ofs += + num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, + archive_name_size) != archive_name_size) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { + uncomp_crc32 = + (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, + buf_size) != buf_size) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + method = MZ_DEFLATED; + } else if (buf_size) { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, + tdefl_create_comp_flags_from_zip_params( + level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != + TDEFL_STATUS_DONE)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header( + pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, + comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, + sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir( + pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, + dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, + const char *pSrc_filename, const void *pComment, + mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint32 ext_attributes) { + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0; +#ifndef MINIZ_NO_TIME + time_t file_modified_time; +#endif + + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, uncomp_size = 0, + comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + MZ_FILE *pSrc_file = NULL; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + if ((!pZip) || (!pZip->m_pState) || + (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || + ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || + ((pZip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + +#ifndef MINIZ_NO_TIME + memset(&file_modified_time, 0, sizeof(file_modified_time)); + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return MZ_FALSE; + mz_zip_time_t_to_dos_time(file_modified_time, &dos_time, &dos_date); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return MZ_FALSE; + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + if (uncomp_size > 0xFFFFFFFF) { + // No zip64 support yet + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, + num_alignment_padding_bytes + + sizeof(local_dir_header))) { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == + 0); + } + cur_archive_file_ofs += + num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, + archive_name_size) != archive_name_size) { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (uncomp_size) { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = + pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + if (!level) { + while (uncomp_remaining) { + mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || + (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, + n) != n)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + uncomp_crc32 = + (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } else { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, + tdefl_create_comp_flags_from_zip_params( + level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + for (;;) { + size_t in_buf_size = + (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + break; + + uncomp_crc32 = (mz_uint32)mz_crc32( + uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, + uncomp_remaining ? TDEFL_NO_FLUSH + : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) { + result = MZ_TRUE; + break; + } else if (status != TDEFL_STATUS_OKAY) + break; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + MZ_FCLOSE(pSrc_file); + pSrc_file = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header( + pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, + comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, + sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir( + pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, + dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, + mz_zip_archive *pSource_zip, + mz_uint file_index) { + mz_uint n, bit_flags, num_alignment_padding_bytes; + mz_uint64 comp_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + if (NULL == + (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) + return MZ_FALSE; + pState = pZip->m_pState; + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || + ((pZip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > + 0xFFFFFFFF)) + return MZ_FALSE; + + cur_src_file_ofs = + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + cur_dst_file_ofs = pZip->m_archive_size; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, + pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, + num_alignment_padding_bytes)) + return MZ_FALSE; + cur_dst_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == + 0); + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + comp_bytes_remaining = + n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + + if (NULL == + (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + (size_t)MZ_MAX(sizeof(mz_uint32) * 4, + MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, + comp_bytes_remaining))))) + return MZ_FALSE; + + while (comp_bytes_remaining) { + n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, + n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_dst_file_ofs += n; + + comp_bytes_remaining -= n; + } + + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) { + // Copy data descriptor + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, + sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + // cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + // no zip64 support yet + if (cur_dst_file_ofs > 0xFFFFFFFF) + return MZ_FALSE; + + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, + local_dir_header_ofs); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return MZ_FALSE; + + n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + if (!mz_zip_array_push_back( + pZip, &pState->m_central_dir, + pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, + MZ_FALSE); + return MZ_FALSE; + } + + if (pState->m_central_dir.m_size > 0xFFFFFFFF) + return MZ_FALSE; + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, + MZ_FALSE); + return MZ_FALSE; + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + + pState = pZip->m_pState; + + // no zip64 support yet + if ((pZip->m_total_files > 0xFFFF) || + ((pZip->m_archive_size + pState->m_central_dir.m_size + + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) { + // Write central directory + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, + pState->m_central_dir.m_p, + (size_t)central_dir_size) != central_dir_size) + return MZ_FALSE; + pZip->m_archive_size += central_dir_size; + } + + // Write end of central directory record + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, + pZip->m_total_files); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, + sizeof(hdr)) != sizeof(hdr)) + return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return MZ_FALSE; +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_archive_size += sizeof(hdr); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, + size_t *pSize) { + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) + return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return MZ_FALSE; + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *pBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || + ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && + (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + return MZ_FALSE; + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place( + const char *pZip_filename, const char *pArchive_name, const void *pBuf, + size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags) { + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + MZ_CLEAR_OBJ(zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || + ((comment_size) && (!pComment)) || + ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { + // Create a new archive. + if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) + return MZ_FALSE; + created_new_archive = MZ_TRUE; + } else { + // Append to an existing archive. + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, + level_and_flags | + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return MZ_FALSE; + if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) { + mz_zip_reader_end(&zip_archive); + return MZ_FALSE; + } + } + status = + mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, + pComment, comment_size, level_and_flags, 0, 0); + // Always finalize, even if adding failed for some reason, so we have a valid + // central directory. (This may not always succeed, but we can try.) + if (!mz_zip_writer_finalize_archive(&zip_archive)) + status = MZ_FALSE; + if (!mz_zip_writer_end(&zip_archive)) + status = MZ_FALSE; + if ((!status) && (created_new_archive)) { + // It's a new archive and something went wrong, so just delete it. + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + return status; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, + const char *pArchive_name, + size_t *pSize, mz_uint flags) { + int file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + return NULL; + + MZ_CLEAR_OBJ(zip_archive); + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, + flags | + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return NULL; + + if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, + flags)) >= 0) + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + + mz_zip_reader_end(&zip_archive); + return p; +} + +#endif // #ifndef MINIZ_NO_STDIO + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_FILE_ONLY + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ diff --git a/android/vendor/kuba-zip/zip.c b/android/vendor/kuba-zip/zip.c new file mode 100644 index 00000000..3e648995 --- /dev/null +++ b/android/vendor/kuba-zip/zip.c @@ -0,0 +1,1622 @@ +/* + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 + +#include +#include +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW32__) +/* Win32, DOS, MSVC, MSVS */ +#include + +#define MKDIR(DIRNAME) _mkdir(DIRNAME) +#define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL) +#define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ + (P)[1] == ':') +#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) + +#else + +#include // needed for symlink() + +#define MKDIR(DIRNAME) mkdir(DIRNAME, 0755) +#define STRCLONE(STR) ((STR) ? strdup(STR) : NULL) + +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#include "miniz.h" +#include "zip.h" + +#ifdef _MSC_VER +#include + +#define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0)) +#define fileno _fileno +#endif + +#ifndef HAS_DEVICE +#define HAS_DEVICE(P) 0 +#endif + +#ifndef FILESYSTEM_PREFIX_LEN +#define FILESYSTEM_PREFIX_LEN(P) 0 +#endif + +#ifndef ISSLASH +#define ISSLASH(C) ((C) == '/' || (C) == '\\') +#endif + +#define CLEANUP(ptr) \ + do { \ + if (ptr) { \ + free((void *)ptr); \ + ptr = NULL; \ + } \ + } while (0) + +struct zip_entry_t { + int index; + char *name; + mz_uint64 uncomp_size; + mz_uint64 comp_size; + mz_uint32 uncomp_crc32; + mz_uint64 offset; + mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint64 header_offset; + mz_uint16 method; + mz_zip_writer_add_state state; + tdefl_compressor comp; + mz_uint32 external_attr; + time_t m_time; +}; + +struct zip_t { + mz_zip_archive archive; + mz_uint level; + struct zip_entry_t entry; +}; + +enum zip_modify_t { + MZ_KEEP = 0, + MZ_DELETE = 1, + MZ_MOVE = 2, +}; + +struct zip_entry_mark_t { + int file_index; + enum zip_modify_t type; + mz_uint64 m_local_header_ofs; + mz_uint64 lf_length; +}; + +static const char *const zip_errlist[30] = { + NULL, + "not initialized\0", + "invalid entry name\0", + "entry not found\0", + "invalid zip mode\0", + "invalid compression level\0", + "no zip 64 support\0", + "memset error\0", + "cannot write data to entry\0", + "cannot initialize tdefl compressor\0", + "invalid index\0", + "header not found\0", + "cannot flush tdefl buffer\0", + "cannot write entry header\0", + "cannot create entry header\0", + "cannot write to central dir\0", + "cannot open file\0", + "invalid entry type\0", + "extracting data using no memory allocation\0", + "file not found\0", + "no permission\0", + "out of memory\0", + "invalid zip archive name\0", + "make dir error\0", + "symlink error\0", + "close archive error\0", + "capacity size too small\0", + "fseek error\0", + "fread error\0", + "fwrite error\0", +}; + +const char *zip_strerror(int errnum) { + errnum = -errnum; + if (errnum <= 0 || errnum >= 30) { + return NULL; + } + + return zip_errlist[errnum]; +} + +static const char *zip_basename(const char *name) { + char const *p; + char const *base = name += FILESYSTEM_PREFIX_LEN(name); + int all_slashes = 1; + + for (p = name; *p; p++) { + if (ISSLASH(*p)) + base = p + 1; + else + all_slashes = 0; + } + + /* If NAME is all slashes, arrange to return `/'. */ + if (*base == '\0' && ISSLASH(*name) && all_slashes) + --base; + + return base; +} + +static int zip_mkpath(char *path) { + char *p; + char npath[MAX_PATH + 1]; + int len = 0; + int has_device = HAS_DEVICE(path); + + memset(npath, 0, MAX_PATH + 1); + if (has_device) { + // only on windows + npath[0] = path[0]; + npath[1] = path[1]; + len = 2; + } + for (p = path + len; *p && len < MAX_PATH; p++) { + if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) { +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW32__) +#else + if ('\\' == *p) { + *p = '/'; + } +#endif + + if (MKDIR(npath) == -1) { + if (errno != EEXIST) { + return ZIP_EMKDIR; + } + } + } + npath[len++] = *p; + } + + return 0; +} + +static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) { + char c; + size_t i; + char *rpl = (char *)calloc((1 + n), sizeof(char)); + char *begin = rpl; + if (!rpl) { + return NULL; + } + + for (i = 0; (i < n) && (c = *str++); ++i) { + if (c == oldchar) { + c = newchar; + } + *rpl++ = c; + } + + return begin; +} + +static char *zip_name_normalize(char *name, char *const nname, size_t len) { + size_t offn = 0; + size_t offnn = 0, ncpy = 0; + + if (name == NULL || nname == NULL || len <= 0) { + return NULL; + } + // skip trailing '/' + while (ISSLASH(*name)) + name++; + + for (; offn < len; offn++) { + if (ISSLASH(name[offn])) { + if (ncpy > 0 && strcmp(&nname[offnn], ".\0") && + strcmp(&nname[offnn], "..\0")) { + offnn += ncpy; + nname[offnn++] = name[offn]; // append '/' + } + ncpy = 0; + } else { + nname[offnn + ncpy] = name[offn]; + ncpy++; + } + } + + // at the end, extra check what we've already copied + if (ncpy == 0 || !strcmp(&nname[offnn], ".\0") || + !strcmp(&nname[offnn], "..\0")) { + nname[offnn] = 0; + } + return nname; +} + +static mz_bool zip_name_match(const char *name1, const char *name2) { + int len2 = strlen(name2); + char *nname2 = zip_strrpl(name2, len2, '\\', '/'); + if (!nname2) { + return MZ_FALSE; + } + + mz_bool res = (strcmp(name1, nname2) == 0) ? MZ_TRUE : MZ_FALSE; + CLEANUP(nname2); + return res; +} + +static int zip_archive_truncate(mz_zip_archive *pzip) { + mz_zip_internal_state *pState = pzip->m_pState; + mz_uint64 file_size = pzip->m_archive_size; + if ((pzip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { + return 0; + } + if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { + if (pState->m_pFile) { + int fd = fileno(pState->m_pFile); + return ftruncate(fd, file_size); + } + } + return 0; +} + +static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir, + int (*on_extract)(const char *filename, + void *arg), + void *arg) { + int err = 0; + mz_uint i, n; + char path[MAX_PATH + 1]; + char symlink_to[MAX_PATH + 1]; + mz_zip_archive_file_stat info; + size_t dirlen = 0; + mz_uint32 xattr = 0; + + memset(path, 0, sizeof(path)); + memset(symlink_to, 0, sizeof(symlink_to)); + + dirlen = strlen(dir); + if (dirlen + 1 > MAX_PATH) { + return ZIP_EINVENTNAME; + } + + memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); + +#if defined(_MSC_VER) + strcpy_s(path, MAX_PATH, dir); +#else + strcpy(path, dir); +#endif + + if (!ISSLASH(path[dirlen - 1])) { +#if defined(_WIN32) || defined(__WIN32__) + path[dirlen] = '\\'; +#else + path[dirlen] = '/'; +#endif + ++dirlen; + } + + // Get and print information about each file in the archive. + n = mz_zip_reader_get_num_files(zip_archive); + for (i = 0; i < n; ++i) { + if (!mz_zip_reader_file_stat(zip_archive, i, &info)) { + // Cannot get information about zip archive; + err = ZIP_ENOENT; + goto out; + } + + if (!zip_name_normalize(info.m_filename, info.m_filename, + strlen(info.m_filename))) { + // Cannot normalize file name; + err = ZIP_EINVENTNAME; + goto out; + } +#if defined(_MSC_VER) + strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename, + MAX_PATH - dirlen); +#else + strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); +#endif + err = zip_mkpath(path); + if (err < 0) { + // Cannot make a path + goto out; + } + + if ((((info.m_version_made_by >> 8) == 3) || + ((info.m_version_made_by >> 8) == + 19)) // if zip is produced on Unix or macOS (3 and 19 from + // section 4.4.2.2 of zip standard) + && info.m_external_attr & + (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 + // is directory) +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW32__) +#else + if (info.m_uncomp_size > MAX_PATH || + !mz_zip_reader_extract_to_mem_no_alloc(zip_archive, i, symlink_to, + MAX_PATH, 0, NULL, 0)) { + err = ZIP_EMEMNOALLOC; + goto out; + } + symlink_to[info.m_uncomp_size] = '\0'; + if (symlink(symlink_to, path) != 0) { + err = ZIP_ESYMLINK; + goto out; + } +#endif + } else { + if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) { + if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) { + // Cannot extract zip archive to file + err = ZIP_ENOFILE; + goto out; + } + } + +#if defined(_MSC_VER) + (void)xattr; // unused +#else + xattr = (info.m_external_attr >> 16) & 0xFFFF; + if (xattr > 0) { + if (chmod(path, (mode_t)xattr) < 0) { + err = ZIP_ENOPERM; + goto out; + } + } +#endif + } + + if (on_extract) { + if (on_extract(path, arg) < 0) { + goto out; + } + } + } + +out: + // Close the archive, freeing any resources it was using + if (!mz_zip_reader_end(zip_archive)) { + // Cannot end zip reader + err = ZIP_ECLSZIP; + } + return err; +} + +static inline void zip_archive_finalize(mz_zip_archive *pzip) { + mz_zip_writer_finalize_archive(pzip); + zip_archive_truncate(pzip); +} + +static int zip_entry_mark(struct zip_t *zip, + struct zip_entry_mark_t *entry_mark, int n, + char *const entries[], const size_t len) { + int err = 0; + if (!zip || !entry_mark || !entries) { + return ZIP_ENOINIT; + } + + mz_zip_archive_file_stat file_stat; + mz_uint64 d_pos = ~0; + for (int i = 0; i < n; ++i) { + + if ((err = zip_entry_openbyindex(zip, i))) { + return err; + } + + mz_bool name_matches = MZ_FALSE; + for (int j = 0; j < (const int)len; ++j) { + if (zip_name_match(zip->entry.name, entries[j])) { + name_matches = MZ_TRUE; + break; + } + } + if (name_matches) { + entry_mark[i].type = MZ_DELETE; + } else { + entry_mark[i].type = MZ_KEEP; + } + + if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) { + return ZIP_ENOENT; + } + + zip_entry_close(zip); + + entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs; + entry_mark[i].file_index = -1; + entry_mark[i].lf_length = 0; + if ((entry_mark[i].type) == MZ_DELETE && + (d_pos > entry_mark[i].m_local_header_ofs)) { + d_pos = entry_mark[i].m_local_header_ofs; + } + } + for (int i = 0; i < n; ++i) { + if ((entry_mark[i].m_local_header_ofs > d_pos) && + (entry_mark[i].type != MZ_DELETE)) { + entry_mark[i].type = MZ_MOVE; + } + } + return err; +} + +static int zip_index_next(mz_uint64 *local_header_ofs_array, int cur_index) { + int new_index = 0; + for (int i = cur_index - 1; i >= 0; --i) { + if (local_header_ofs_array[cur_index] > local_header_ofs_array[i]) { + new_index = i + 1; + return new_index; + } + } + return new_index; +} + +static int zip_sort(mz_uint64 *local_header_ofs_array, int cur_index) { + int nxt_index = zip_index_next(local_header_ofs_array, cur_index); + + if (nxt_index != cur_index) { + mz_uint64 temp = local_header_ofs_array[cur_index]; + for (int i = cur_index; i > nxt_index; i--) { + local_header_ofs_array[i] = local_header_ofs_array[i - 1]; + } + local_header_ofs_array[nxt_index] = temp; + } + return nxt_index; +} + +static int zip_index_update(struct zip_entry_mark_t *entry_mark, int last_index, + int nxt_index) { + for (int j = 0; j < last_index; j++) { + if (entry_mark[j].file_index >= nxt_index) { + entry_mark[j].file_index += 1; + } + } + entry_mark[nxt_index].file_index = last_index; + return 0; +} + +static int zip_entry_finalize(struct zip_t *zip, + struct zip_entry_mark_t *entry_mark, + const int n) { + + mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); + if (!local_header_ofs_array) { + return ZIP_EOOMEM; + } + + for (int i = 0; i < n; ++i) { + local_header_ofs_array[i] = entry_mark[i].m_local_header_ofs; + int index = zip_sort(local_header_ofs_array, i); + + if (index != i) { + zip_index_update(entry_mark, i, index); + } + entry_mark[i].file_index = index; + } + + mz_uint64 *length = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); + if (!length) { + CLEANUP(local_header_ofs_array); + return ZIP_EOOMEM; + } + for (int i = 0; i < n - 1; i++) { + length[i] = local_header_ofs_array[i + 1] - local_header_ofs_array[i]; + } + length[n - 1] = zip->archive.m_archive_size - local_header_ofs_array[n - 1]; + + for (int i = 0; i < n; i++) { + entry_mark[i].lf_length = length[entry_mark[i].file_index]; + } + + CLEANUP(length); + CLEANUP(local_header_ofs_array); + return 0; +} + +static int zip_entry_set(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, + int n, char *const entries[], const size_t len) { + int err = 0; + + if ((err = zip_entry_mark(zip, entry_mark, n, entries, len)) < 0) { + return err; + } + if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) { + return err; + } + return 0; +} + +static mz_int64 zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to, + const mz_uint64 from, const mz_uint64 length, + mz_uint8 *move_buf, + const mz_int64 capacity_size) { + if ((mz_int64)length > capacity_size) { + return ZIP_ECAPSIZE; + } + if (MZ_FSEEK64(m_pFile, from, SEEK_SET)) { + MZ_FCLOSE(m_pFile); + return ZIP_EFSEEK; + } + + if (fread(move_buf, 1, length, m_pFile) != length) { + MZ_FCLOSE(m_pFile); + return ZIP_EFREAD; + } + if (MZ_FSEEK64(m_pFile, to, SEEK_SET)) { + MZ_FCLOSE(m_pFile); + return ZIP_EFSEEK; + } + if (fwrite(move_buf, 1, length, m_pFile) != length) { + MZ_FCLOSE(m_pFile); + return ZIP_EFWRITE; + } + return (mz_int64)length; +} + +static mz_int64 zip_files_move(MZ_FILE *m_pFile, mz_uint64 writen_num, + mz_uint64 read_num, mz_uint64 length) { + int n = 0; + const mz_int64 page_size = 1 << 12; // 4K + mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size); + if (move_buf == NULL) { + return ZIP_EOOMEM; + } + + mz_int64 moved_length = 0; + mz_int64 move_count = 0; + while ((mz_int64)length > 0) { + move_count = ((mz_int64)length >= page_size) ? page_size : (mz_int64)length; + n = zip_file_move(m_pFile, writen_num, read_num, move_count, move_buf, + page_size); + if (n < 0) { + moved_length = n; + goto cleanup; + } + + if (n != move_count) { + goto cleanup; + } + + writen_num += move_count; + read_num += move_count; + length -= move_count; + moved_length += move_count; + } + +cleanup: + CLEANUP(move_buf); + return moved_length; +} + +static int zip_central_dir_move(mz_zip_internal_state *pState, int begin, + int end, int entry_num) { + if (begin == entry_num) { + return 0; + } + + mz_uint64 l_size = 0; + mz_uint64 r_size = 0; + mz_uint64 d_size = 0; + mz_uint8 *next = NULL; + mz_uint8 *deleted = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin)); + l_size = (mz_uint32)(deleted - (mz_uint8 *)(pState->m_central_dir.m_p)); + if (end == entry_num) { + r_size = 0; + } else { + next = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, end)); + r_size = pState->m_central_dir.m_size - + (mz_uint32)(next - (mz_uint8 *)(pState->m_central_dir.m_p)); + d_size = next - deleted; + } + + if (l_size == 0) { + memmove(pState->m_central_dir.m_p, next, r_size); + pState->m_central_dir.m_p = MZ_REALLOC(pState->m_central_dir.m_p, r_size); + for (int i = end; i < entry_num; i++) { + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -= + d_size; + } + } + + if (l_size * r_size != 0) { + memmove(deleted, next, r_size); + for (int i = end; i < entry_num; i++) { + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -= + d_size; + } + } + + pState->m_central_dir.m_size = l_size + r_size; + return 0; +} + +static int zip_central_dir_delete(mz_zip_internal_state *pState, + int *deleted_entry_index_array, + int entry_num) { + int i = 0; + int begin = 0; + int end = 0; + int d_num = 0; + while (i < entry_num) { + while ((!deleted_entry_index_array[i]) && (i < entry_num)) { + i++; + } + begin = i; + + while ((deleted_entry_index_array[i]) && (i < entry_num)) { + i++; + } + end = i; + zip_central_dir_move(pState, begin, end, entry_num); + } + + i = 0; + while (i < entry_num) { + while ((!deleted_entry_index_array[i]) && (i < entry_num)) { + i++; + } + begin = i; + if (begin == entry_num) { + break; + } + while ((deleted_entry_index_array[i]) && (i < entry_num)) { + i++; + } + end = i; + int k = 0; + for (int j = end; j < entry_num; j++) { + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, + begin + k) = + (mz_uint32)MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, + mz_uint32, j); + k++; + } + d_num += end - begin; + } + + pState->m_central_dir_offsets.m_size = + sizeof(mz_uint32) * (entry_num - d_num); + return 0; +} + +static int zip_entries_delete_mark(struct zip_t *zip, + struct zip_entry_mark_t *entry_mark, + int entry_num) { + mz_uint64 writen_num = 0; + mz_uint64 read_num = 0; + mz_uint64 deleted_length = 0; + mz_uint64 move_length = 0; + int i = 0; + int deleted_entry_num = 0; + int n = 0; + + mz_bool *deleted_entry_flag_array = + (mz_bool *)calloc(entry_num, sizeof(mz_bool)); + if (deleted_entry_flag_array == NULL) { + return ZIP_EOOMEM; + } + + mz_zip_internal_state *pState = zip->archive.m_pState; + zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING; + + if (MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) { + CLEANUP(deleted_entry_flag_array); + return ZIP_ENOENT; + } + + while (i < entry_num) { + while ((entry_mark[i].type == MZ_KEEP) && (i < entry_num)) { + writen_num += entry_mark[i].lf_length; + read_num = writen_num; + i++; + } + + while ((entry_mark[i].type == MZ_DELETE) && (i < entry_num)) { + deleted_entry_flag_array[i] = MZ_TRUE; + read_num += entry_mark[i].lf_length; + deleted_length += entry_mark[i].lf_length; + i++; + deleted_entry_num++; + } + + while ((entry_mark[i].type == MZ_MOVE) && (i < entry_num)) { + move_length += entry_mark[i].lf_length; + mz_uint8 *p = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i)); + if (!p) { + CLEANUP(deleted_entry_flag_array); + return ZIP_ENOENT; + } + mz_uint32 offset = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + offset -= (mz_uint32)deleted_length; + MZ_WRITE_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS, offset); + i++; + } + + n = zip_files_move(pState->m_pFile, writen_num, read_num, move_length); + if (n != (mz_int64)move_length) { + CLEANUP(deleted_entry_flag_array); + return n; + } + writen_num += move_length; + read_num += move_length; + } + + zip->archive.m_archive_size -= deleted_length; + zip->archive.m_total_files = entry_num - deleted_entry_num; + + zip_central_dir_delete(pState, deleted_entry_flag_array, entry_num); + CLEANUP(deleted_entry_flag_array); + + return deleted_entry_num; +} + +struct zip_t *zip_open(const char *zipname, int level, char mode) { + struct zip_t *zip = NULL; + + if (!zipname || strlen(zipname) < 1) { + // zip_t archive name is empty or NULL + goto cleanup; + } + + if (level < 0) + level = MZ_DEFAULT_LEVEL; + if ((level & 0xF) > MZ_UBER_COMPRESSION) { + // Wrong compression level + goto cleanup; + } + + zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); + if (!zip) + goto cleanup; + + zip->level = (mz_uint)level; + switch (mode) { + case 'w': + // Create a new archive. + if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) { + // Cannot initialize zip_archive writer + goto cleanup; + } + break; + + case 'r': + case 'a': + case 'd': + if (!mz_zip_reader_init_file( + &(zip->archive), zipname, + zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { + // An archive file does not exist or cannot initialize + // zip_archive reader + goto cleanup; + } + if ((mode == 'a' || mode == 'd') && + !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { + mz_zip_reader_end(&(zip->archive)); + goto cleanup; + } + break; + + default: + goto cleanup; + } + + return zip; + +cleanup: + CLEANUP(zip); + return NULL; +} + +void zip_close(struct zip_t *zip) { + if (zip) { + // Always finalize, even if adding failed for some reason, so we have a + // valid central directory. + mz_zip_writer_finalize_archive(&(zip->archive)); + zip_archive_truncate(&(zip->archive)); + mz_zip_writer_end(&(zip->archive)); + mz_zip_reader_end(&(zip->archive)); + + CLEANUP(zip); + } +} + +int zip_is64(struct zip_t *zip) { + if (!zip || !zip->archive.m_pState) { + // zip_t handler or zip state is not initialized + return ZIP_ENOINIT; + } + + return (int)zip->archive.m_pState->m_zip64; +} + +int zip_entry_open(struct zip_t *zip, const char *entryname) { + size_t entrylen = 0; + mz_zip_archive *pzip = NULL; + mz_uint num_alignment_padding_bytes, level; + mz_zip_archive_file_stat stats; + int err = 0; + + if (!zip) { + return ZIP_ENOINIT; + } + + if (!entryname) { + return ZIP_EINVENTNAME; + } + + entrylen = strlen(entryname); + if (entrylen == 0) { + return ZIP_EINVENTNAME; + } + + /* + .ZIP File Format Specification Version: 6.3.3 + + 4.4.17.1 The name of the file, with optional relative path. + The path stored MUST not contain a drive or + device letter, or a leading slash. All slashes + MUST be forward slashes '/' as opposed to + backwards slashes '\' for compatibility with Amiga + and UNIX file systems etc. If input came from standard + input, there is no file name field. + */ + if (zip->entry.name) { + CLEANUP(zip->entry.name); + } + zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/'); + if (!zip->entry.name) { + // Cannot parse zip entry name + return ZIP_EINVENTNAME; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { + zip->entry.index = + mz_zip_reader_locate_file(pzip, zip->entry.name, NULL, 0); + if (zip->entry.index < 0) { + err = ZIP_ENOENT; + goto cleanup; + } + + if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) { + err = ZIP_ENOENT; + goto cleanup; + } + + zip->entry.comp_size = stats.m_comp_size; + zip->entry.uncomp_size = stats.m_uncomp_size; + zip->entry.uncomp_crc32 = stats.m_crc32; + zip->entry.offset = stats.m_central_dir_ofs; + zip->entry.header_offset = stats.m_local_header_ofs; + zip->entry.method = stats.m_method; + zip->entry.external_attr = stats.m_external_attr; +#ifndef MINIZ_NO_TIME + zip->entry.m_time = stats.m_time; +#endif + + return 0; + } + + zip->entry.index = (int)zip->archive.m_total_files; + zip->entry.comp_size = 0; + zip->entry.uncomp_size = 0; + zip->entry.uncomp_crc32 = MZ_CRC32_INIT; + zip->entry.offset = zip->archive.m_archive_size; + zip->entry.header_offset = zip->archive.m_archive_size; + memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); + zip->entry.method = 0; + + // UNIX or APPLE +#if MZ_PLATFORM == 3 || MZ_PLATFORM == 19 + // regular file with rw-r--r-- persmissions + zip->entry.external_attr = (mz_uint32)(0100644) << 16; +#else + zip->entry.external_attr = 0; +#endif + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); + + if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { + // Invalid zip mode + err = ZIP_EINVMODE; + goto cleanup; + } + if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { + // Invalid zip compression level + err = ZIP_EINVLVL; + goto cleanup; + } + // no zip64 support yet + if ((pzip->m_total_files == 0xFFFF) || + ((pzip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + entrylen) > 0xFFFFFFFF)) { + // No zip64 support yet + err = ZIP_ENOSUP64; + goto cleanup; + } + if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset, + num_alignment_padding_bytes + + sizeof(zip->entry.header))) { + // Cannot memset zip entry header + err = ZIP_EMEMSET; + goto cleanup; + } + + zip->entry.header_offset += num_alignment_padding_bytes; + if (pzip->m_file_offset_alignment) { + MZ_ASSERT( + (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0); + } + zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header); + + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, + entrylen) != entrylen) { + // Cannot write data to zip entry + err = ZIP_EWRTENT; + goto cleanup; + } + + zip->entry.offset += entrylen; + level = zip->level & 0xF; + if (level) { + zip->entry.state.m_pZip = pzip; + zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset; + zip->entry.state.m_comp_size = 0; + + if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, + &(zip->entry.state), + (int)tdefl_create_comp_flags_from_zip_params( + (int)level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) { + // Cannot initialize the zip compressor + err = ZIP_ETDEFLINIT; + goto cleanup; + } + } + + zip->entry.m_time = time(NULL); + + return 0; + +cleanup: + CLEANUP(zip->entry.name); + return err; +} + +int zip_entry_openbyindex(struct zip_t *zip, int index) { + mz_zip_archive *pZip = NULL; + mz_zip_archive_file_stat stats; + mz_uint namelen; + const mz_uint8 *pHeader; + const char *pFilename; + + if (!zip) { + // zip_t handler is not initialized + return ZIP_ENOINIT; + } + + pZip = &(zip->archive); + if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) { + // open by index requires readonly mode + return ZIP_EINVMODE; + } + + if (index < 0 || (mz_uint)index >= pZip->m_total_files) { + // index out of range + return ZIP_EINVIDX; + } + + if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, + mz_uint32, index)))) { + // cannot find header in central directory + return ZIP_ENOHDR; + } + + namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + + /* + .ZIP File Format Specification Version: 6.3.3 + + 4.4.17.1 The name of the file, with optional relative path. + The path stored MUST not contain a drive or + device letter, or a leading slash. All slashes + MUST be forward slashes '/' as opposed to + backwards slashes '\' for compatibility with Amiga + and UNIX file systems etc. If input came from standard + input, there is no file name field. + */ + if (zip->entry.name) { + CLEANUP(zip->entry.name); + } + zip->entry.name = zip_strrpl(pFilename, namelen, '\\', '/'); + if (!zip->entry.name) { + // local entry name is NULL + return ZIP_EINVENTNAME; + } + + if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) { + return ZIP_ENOENT; + } + + zip->entry.index = index; + zip->entry.comp_size = stats.m_comp_size; + zip->entry.uncomp_size = stats.m_uncomp_size; + zip->entry.uncomp_crc32 = stats.m_crc32; + zip->entry.offset = stats.m_central_dir_ofs; + zip->entry.header_offset = stats.m_local_header_ofs; + zip->entry.method = stats.m_method; + zip->entry.external_attr = stats.m_external_attr; +#ifndef MINIZ_NO_TIME + zip->entry.m_time = stats.m_time; +#endif + + return 0; +} + +int zip_entry_close(struct zip_t *zip) { + mz_zip_archive *pzip = NULL; + mz_uint level; + tdefl_status done; + mz_uint16 entrylen; + mz_uint16 dos_time = 0, dos_date = 0; + int err = 0; + + if (!zip) { + // zip_t handler is not initialized + err = ZIP_ENOINIT; + goto cleanup; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { + goto cleanup; + } + + level = zip->level & 0xF; + if (level) { + done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); + if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { + // Cannot flush compressed buffer + err = ZIP_ETDEFLBUF; + goto cleanup; + } + zip->entry.comp_size = zip->entry.state.m_comp_size; + zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs; + zip->entry.method = MZ_DEFLATED; + } + + entrylen = (mz_uint16)strlen(zip->entry.name); + if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) { + // No zip64 support, yet + err = ZIP_ENOSUP64; + goto cleanup; + } + +#ifndef MINIZ_NO_TIME + mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); +#endif + + if (!mz_zip_writer_create_local_dir_header( + pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, + zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, + dos_time, dos_date)) { + // Cannot create zip entry header + err = ZIP_ECRTHDR; + goto cleanup; + } + + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, + zip->entry.header, + sizeof(zip->entry.header)) != sizeof(zip->entry.header)) { + // Cannot write zip entry header + err = ZIP_EWRTHDR; + goto cleanup; + } + + if (!mz_zip_writer_add_to_central_dir( + pzip, zip->entry.name, entrylen, NULL, 0, "", 0, + zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, + zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset, + zip->entry.external_attr)) { + // Cannot write to zip central dir + err = ZIP_EWRTDIR; + goto cleanup; + } + + pzip->m_total_files++; + pzip->m_archive_size = zip->entry.offset; + +cleanup: + if (zip) { + zip->entry.m_time = 0; + CLEANUP(zip->entry.name); + } + return err; +} + +const char *zip_entry_name(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return NULL; + } + + return zip->entry.name; +} + +int zip_entry_index(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return ZIP_ENOINIT; + } + + return zip->entry.index; +} + +int zip_entry_isdir(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return ZIP_ENOINIT; + } + + if (zip->entry.index < 0) { + // zip entry is not opened + return ZIP_EINVIDX; + } + + return (int)mz_zip_reader_is_file_a_directory(&zip->archive, + (mz_uint)zip->entry.index); +} + +unsigned long long zip_entry_size(struct zip_t *zip) { + return zip ? zip->entry.uncomp_size : 0; +} + +unsigned int zip_entry_crc32(struct zip_t *zip) { + return zip ? zip->entry.uncomp_crc32 : 0; +} + +int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { + mz_uint level; + mz_zip_archive *pzip = NULL; + tdefl_status status; + + if (!zip) { + // zip_t handler is not initialized + return ZIP_ENOINIT; + } + + pzip = &(zip->archive); + if (buf && bufsize > 0) { + zip->entry.uncomp_size += bufsize; + zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32( + zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize); + + level = zip->level & 0xF; + if (!level) { + if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf, + bufsize) != bufsize)) { + // Cannot write buffer + return ZIP_EWRTENT; + } + zip->entry.offset += bufsize; + zip->entry.comp_size += bufsize; + } else { + status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, + TDEFL_NO_FLUSH); + if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) { + // Cannot compress buffer + return ZIP_ETDEFLBUF; + } + } + } + + return 0; +} + +int zip_entry_fwrite(struct zip_t *zip, const char *filename) { + int err = 0; + size_t n = 0; + FILE *stream = NULL; + mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE]; + struct MZ_FILE_STAT_STRUCT file_stat; + + if (!zip) { + // zip_t handler is not initialized + return ZIP_ENOINIT; + } + + memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE); + memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT)); + if (MZ_FILE_STAT(filename, &file_stat) != 0) { + // problem getting information - check errno + return ZIP_ENOENT; + } + + if ((file_stat.st_mode & 0200) == 0) { + // MS-DOS read-only attribute + zip->entry.external_attr |= 0x01; + } + zip->entry.external_attr |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16); + zip->entry.m_time = file_stat.st_mtime; + +#if defined(_MSC_VER) + if (fopen_s(&stream, filename, "rb")) +#else + if (!(stream = fopen(filename, "rb"))) +#endif + { + // Cannot open filename + return ZIP_EOPNFILE; + } + + while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > + 0) { + if (zip_entry_write(zip, buf, n) < 0) { + err = ZIP_EWRTENT; + break; + } + } + fclose(stream); + + return err; +} + +ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + size_t size = 0; + + if (!zip) { + // zip_t handler is not initialized + return (ssize_t)ZIP_ENOINIT; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return (ssize_t)ZIP_ENOENT; + } + + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return (ssize_t)ZIP_EINVENTTYPE; + } + + *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0); + if (*buf && bufsize) { + *bufsize = size; + } + return (ssize_t)size; +} + +ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { + mz_zip_archive *pzip = NULL; + + if (!zip) { + // zip_t handler is not initialized + return (ssize_t)ZIP_ENOINIT; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return (ssize_t)ZIP_ENOENT; + } + + if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index, + buf, bufsize, 0, NULL, 0)) { + return (ssize_t)ZIP_EMEMNOALLOC; + } + + return (ssize_t)zip->entry.uncomp_size; +} + +int zip_entry_fread(struct zip_t *zip, const char *filename) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + mz_uint32 xattr = 0; + mz_zip_archive_file_stat info; + + if (!zip) { + // zip_t handler is not initialized + return ZIP_ENOINIT; + } + + memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); + pzip = &(zip->archive); + if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return ZIP_ENOENT; + } + + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return ZIP_EINVENTTYPE; + } + + if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) { + return ZIP_ENOFILE; + } + +#if defined(_MSC_VER) + (void)xattr; // unused +#else + if (!mz_zip_reader_file_stat(pzip, idx, &info)) { + // Cannot get information about zip archive; + return ZIP_ENOFILE; + } + + xattr = (info.m_external_attr >> 16) & 0xFFFF; + if (xattr > 0) { + if (chmod(filename, (mode_t)xattr) < 0) { + return ZIP_ENOPERM; + } + } +#endif + + return 0; +} + +int zip_entry_extract(struct zip_t *zip, + size_t (*on_extract)(void *arg, unsigned long long offset, + const void *buf, size_t bufsize), + void *arg) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return ZIP_ENOINIT; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return ZIP_ENOENT; + } + + idx = (mz_uint)zip->entry.index; + return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) + ? 0 + : ZIP_EINVIDX; +} + +int zip_entries_total(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return ZIP_ENOINIT; + } + + return (int)zip->archive.m_total_files; +} + +int zip_entries_delete(struct zip_t *zip, char *const entries[], + const size_t len) { + int n = 0; + int err = 0; + struct zip_entry_mark_t *entry_mark = NULL; + + if (zip == NULL || (entries == NULL && len != 0)) { + return ZIP_ENOINIT; + } + + if (entries == NULL && len == 0) { + return 0; + } + + n = zip_entries_total(zip); + + entry_mark = + (struct zip_entry_mark_t *)calloc(n, sizeof(struct zip_entry_mark_t)); + if (!entry_mark) { + return ZIP_EOOMEM; + } + + zip->archive.m_zip_mode = MZ_ZIP_MODE_READING; + + err = zip_entry_set(zip, entry_mark, n, entries, len); + if (err < 0) { + CLEANUP(entry_mark); + return err; + } + + err = zip_entries_delete_mark(zip, entry_mark, n); + CLEANUP(entry_mark); + return err; +} + +int zip_stream_extract(const char *stream, size_t size, const char *dir, + int (*on_extract)(const char *filename, void *arg), + void *arg) { + mz_zip_archive zip_archive; + if (!stream || !dir) { + // Cannot parse zip archive stream + return ZIP_ENOINIT; + } + if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { + // Cannot memset zip archive + return ZIP_EMEMSET; + } + if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) { + // Cannot initialize zip_archive reader + return ZIP_ENOINIT; + } + + return zip_archive_extract(&zip_archive, dir, on_extract, arg); +} + +struct zip_t *zip_stream_open(const char *stream, size_t size, int level, + char mode) { + struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); + if (!zip) { + return NULL; + } + + if (level < 0) { + level = MZ_DEFAULT_LEVEL; + } + if ((level & 0xF) > MZ_UBER_COMPRESSION) { + // Wrong compression level + goto cleanup; + } + zip->level = (mz_uint)level; + + if ((stream != NULL) && (size > 0) && (mode == 'r')) { + if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) { + goto cleanup; + } + } else if ((stream == NULL) && (size == 0) && (mode == 'w')) { + // Create a new archive. + if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) { + // Cannot initialize zip_archive writer + goto cleanup; + } + } else { + goto cleanup; + } + return zip; + +cleanup: + CLEANUP(zip); + return NULL; +} + +ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize) { + if (!zip) { + return (ssize_t)ZIP_ENOINIT; + } + + zip_archive_finalize(&(zip->archive)); + + if (bufsize != NULL) { + *bufsize = (size_t)zip->archive.m_archive_size; + } + *buf = calloc(sizeof(unsigned char), zip->archive.m_archive_size); + memcpy(*buf, zip->archive.m_pState->m_pMem, zip->archive.m_archive_size); + + return (ssize_t)zip->archive.m_archive_size; +} + +void zip_stream_close(struct zip_t *zip) { + if (zip) { + mz_zip_writer_end(&(zip->archive)); + mz_zip_reader_end(&(zip->archive)); + CLEANUP(zip); + } +} + +int zip_create(const char *zipname, const char *filenames[], size_t len) { + int err = 0; + size_t i; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_uint32 ext_attributes = 0; + + if (!zipname || strlen(zipname) < 1) { + // zip_t archive name is empty or NULL + return ZIP_EINVZIPNAME; + } + + // Create a new archive. + if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { + // Cannot memset zip archive + return ZIP_EMEMSET; + } + + if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) { + // Cannot initialize zip_archive writer + return ZIP_ENOINIT; + } + + if (!memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT))) { + return ZIP_EMEMSET; + } + + for (i = 0; i < len; ++i) { + const char *name = filenames[i]; + if (!name) { + err = ZIP_EINVENTNAME; + break; + } + + if (MZ_FILE_STAT(name, &file_stat) != 0) { + // problem getting information - check errno + err = ZIP_ENOFILE; + break; + } + + if ((file_stat.st_mode & 0200) == 0) { + // MS-DOS read-only attribute + ext_attributes |= 0x01; + } + ext_attributes |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16); + + if (!mz_zip_writer_add_file(&zip_archive, zip_basename(name), name, "", 0, + ZIP_DEFAULT_COMPRESSION_LEVEL, + ext_attributes)) { + // Cannot add file to zip_archive + err = ZIP_ENOFILE; + break; + } + } + + mz_zip_writer_finalize_archive(&zip_archive); + mz_zip_writer_end(&zip_archive); + return err; +} + +int zip_extract(const char *zipname, const char *dir, + int (*on_extract)(const char *filename, void *arg), void *arg) { + mz_zip_archive zip_archive; + + if (!zipname || !dir) { + // Cannot parse zip archive name + return ZIP_EINVZIPNAME; + } + + if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { + // Cannot memset zip archive + return ZIP_EMEMSET; + } + + // Now try to open the archive. + if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { + // Cannot initialize zip_archive reader + return ZIP_ENOINIT; + } + + return zip_archive_extract(&zip_archive, dir, on_extract, arg); +} diff --git a/android/vendor/kuba-zip/zip.h b/android/vendor/kuba-zip/zip.h new file mode 100644 index 00000000..54b412d8 --- /dev/null +++ b/android/vendor/kuba-zip/zip.h @@ -0,0 +1,433 @@ +/* + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef ZIP_H +#define ZIP_H + +#include +#include + +#ifndef ZIP_SHARED +# define ZIP_EXPORT +#else +# ifdef _WIN32 +# ifdef ZIP_BUILD_SHARED +# define ZIP_EXPORT __declspec(dllexport) +# else +# define ZIP_EXPORT __declspec(dllimport) +# endif +# else +# define ZIP_EXPORT __attribute__ ((visibility ("default"))) +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_POSIX_C_SOURCE) && defined(_MSC_VER) +// 64-bit Windows is the only mainstream platform +// where sizeof(long) != sizeof(void*) +#ifdef _WIN64 +typedef long long ssize_t; /* byte count or error */ +#else +typedef long ssize_t; /* byte count or error */ +#endif +#endif + +#ifndef MAX_PATH +#define MAX_PATH 32767 /* # chars in a path name including NULL */ +#endif + +/** + * @mainpage + * + * Documenation for @ref zip. + */ + +/** + * @addtogroup zip + * @{ + */ + +/** + * Default zip compression level. + */ +#define ZIP_DEFAULT_COMPRESSION_LEVEL 6 + +/** + * Error codes + */ +#define ZIP_ENOINIT -1 // not initialized +#define ZIP_EINVENTNAME -2 // invalid entry name +#define ZIP_ENOENT -3 // entry not found +#define ZIP_EINVMODE -4 // invalid zip mode +#define ZIP_EINVLVL -5 // invalid compression level +#define ZIP_ENOSUP64 -6 // no zip 64 support +#define ZIP_EMEMSET -7 // memset error +#define ZIP_EWRTENT -8 // cannot write data to entry +#define ZIP_ETDEFLINIT -9 // cannot initialize tdefl compressor +#define ZIP_EINVIDX -10 // invalid index +#define ZIP_ENOHDR -11 // header not found +#define ZIP_ETDEFLBUF -12 // cannot flush tdefl buffer +#define ZIP_ECRTHDR -13 // cannot create entry header +#define ZIP_EWRTHDR -14 // cannot write entry header +#define ZIP_EWRTDIR -15 // cannot write to central dir +#define ZIP_EOPNFILE -16 // cannot open file +#define ZIP_EINVENTTYPE -17 // invalid entry type +#define ZIP_EMEMNOALLOC -18 // extracting data using no memory allocation +#define ZIP_ENOFILE -19 // file not found +#define ZIP_ENOPERM -20 // no permission +#define ZIP_EOOMEM -21 // out of memory +#define ZIP_EINVZIPNAME -22 // invalid zip archive name +#define ZIP_EMKDIR -23 // make dir error +#define ZIP_ESYMLINK -24 // symlink error +#define ZIP_ECLSZIP -25 // close archive error +#define ZIP_ECAPSIZE -26 // capacity size too small +#define ZIP_EFSEEK -27 // fseek error +#define ZIP_EFREAD -28 // fread error +#define ZIP_EFWRITE -29 // fwrite error + +/** + * Looks up the error message string coresponding to an error number. + * @param errnum error number + * @return error message string coresponding to errnum or NULL if error is not + * found. + */ +extern ZIP_EXPORT const char *zip_strerror(int errnum); + +/** + * @struct zip_t + * + * This data structure is used throughout the library to represent zip archive - + * forward declaration. + */ +struct zip_t; + +/** + * Opens zip archive with compression level using the given mode. + * + * @param zipname zip archive file name. + * @param level compression level (0-9 are the standard zlib-style levels). + * @param mode file access mode. + * - 'r': opens a file for reading/extracting (the file must exists). + * - 'w': creates an empty file for writing. + * - 'a': appends to an existing archive. + * + * @return the zip archive handler or NULL on error + */ +extern ZIP_EXPORT struct zip_t *zip_open(const char *zipname, int level, + char mode); + +/** + * Closes the zip archive, releases resources - always finalize. + * + * @param zip zip archive handler. + */ +extern ZIP_EXPORT void zip_close(struct zip_t *zip); + +/** + * Determines if the archive has a zip64 end of central directory headers. + * + * @param zip zip archive handler. + * + * @return the return code - 1 (true), 0 (false), negative number (< 0) on + * error. + */ +extern ZIP_EXPORT int zip_is64(struct zip_t *zip); + +/** + * Opens an entry by name in the zip archive. + * + * For zip archive opened in 'w' or 'a' mode the function will append + * a new entry. In readonly mode the function tries to locate the entry + * in global dictionary. + * + * @param zip zip archive handler. + * @param entryname an entry name in local dictionary. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_entry_open(struct zip_t *zip, const char *entryname); + +/** + * Opens a new entry by index in the zip archive. + * + * This function is only valid if zip archive was opened in 'r' (readonly) mode. + * + * @param zip zip archive handler. + * @param index index in local dictionary. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_entry_openbyindex(struct zip_t *zip, int index); + +/** + * Closes a zip entry, flushes buffer and releases resources. + * + * @param zip zip archive handler. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_entry_close(struct zip_t *zip); + +/** + * Returns a local name of the current zip entry. + * + * The main difference between user's entry name and local entry name + * is optional relative path. + * Following .ZIP File Format Specification - the path stored MUST not contain + * a drive or device letter, or a leading slash. + * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' + * for compatibility with Amiga and UNIX file systems etc. + * + * @param zip: zip archive handler. + * + * @return the pointer to the current zip entry name, or NULL on error. + */ +extern ZIP_EXPORT const char *zip_entry_name(struct zip_t *zip); + +/** + * Returns an index of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the index on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_entry_index(struct zip_t *zip); + +/** + * Determines if the current zip entry is a directory entry. + * + * @param zip zip archive handler. + * + * @return the return code - 1 (true), 0 (false), negative number (< 0) on + * error. + */ +extern ZIP_EXPORT int zip_entry_isdir(struct zip_t *zip); + +/** + * Returns an uncompressed size of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the uncompressed size in bytes. + */ +extern ZIP_EXPORT unsigned long long zip_entry_size(struct zip_t *zip); + +/** + * Returns CRC-32 checksum of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the CRC-32 checksum. + */ +extern ZIP_EXPORT unsigned int zip_entry_crc32(struct zip_t *zip); + +/** + * Compresses an input buffer for the current zip entry. + * + * @param zip zip archive handler. + * @param buf input buffer. + * @param bufsize input buffer size (in bytes). + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_entry_write(struct zip_t *zip, const void *buf, + size_t bufsize); + +/** + * Compresses a file for the current zip entry. + * + * @param zip zip archive handler. + * @param filename input file. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_entry_fwrite(struct zip_t *zip, const char *filename); + +/** + * Extracts the current zip entry into output buffer. + * + * The function allocates sufficient memory for a output buffer. + * + * @param zip zip archive handler. + * @param buf output buffer. + * @param bufsize output buffer size (in bytes). + * + * @note remember to release memory allocated for a output buffer. + * for large entries, please take a look at zip_entry_extract function. + * + * @return the return code - the number of bytes actually read on success. + * Otherwise a negative number (< 0) on error. + */ +extern ZIP_EXPORT ssize_t zip_entry_read(struct zip_t *zip, void **buf, + size_t *bufsize); + +/** + * Extracts the current zip entry into a memory buffer using no memory + * allocation. + * + * @param zip zip archive handler. + * @param buf preallocated output buffer. + * @param bufsize output buffer size (in bytes). + * + * @note ensure supplied output buffer is large enough. + * zip_entry_size function (returns uncompressed size for the current + * entry) can be handy to estimate how big buffer is needed. + * For large entries, please take a look at zip_entry_extract function. + * + * @return the return code - the number of bytes actually read on success. + * Otherwise a negative number (< 0) on error (e.g. bufsize is not large enough). + */ +extern ZIP_EXPORT ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, + size_t bufsize); + +/** + * Extracts the current zip entry into output file. + * + * @param zip zip archive handler. + * @param filename output file. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_entry_fread(struct zip_t *zip, const char *filename); + +/** + * Extracts the current zip entry using a callback function (on_extract). + * + * @param zip zip archive handler. + * @param on_extract callback function. + * @param arg opaque pointer (optional argument, which you can pass to the + * on_extract callback) + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int +zip_entry_extract(struct zip_t *zip, + size_t (*on_extract)(void *arg, unsigned long long offset, + const void *data, size_t size), + void *arg); + +/** + * Returns the number of all entries (files and directories) in the zip archive. + * + * @param zip zip archive handler. + * + * @return the return code - the number of entries on success, negative number + * (< 0) on error. + */ +extern ZIP_EXPORT int zip_entries_total(struct zip_t *zip); + +/** + * Deletes zip archive entries. + * + * @param zip zip archive handler. + * @param entries array of zip archive entries to be deleted. + * @param len the number of entries to be deleted. + * @return the number of deleted entries, or negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_entries_delete(struct zip_t *zip, + char *const entries[], size_t len); + +/** + * Extracts a zip archive stream into directory. + * + * If on_extract is not NULL, the callback will be called after + * successfully extracted each zip entry. + * Returning a negative value from the callback will cause abort and return an + * error. The last argument (void *arg) is optional, which you can use to pass + * data to the on_extract callback. + * + * @param stream zip archive stream. + * @param size stream size. + * @param dir output directory. + * @param on_extract on extract callback. + * @param arg opaque pointer. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int +zip_stream_extract(const char *stream, size_t size, const char *dir, + int (*on_extract)(const char *filename, void *arg), + void *arg); + +/** + * Opens zip archive stream into memory. + * + * @param stream zip archive stream. + * @param size stream size. + * + * @return the zip archive handler or NULL on error + */ +extern ZIP_EXPORT struct zip_t *zip_stream_open(const char *stream, size_t size, + int level, char mode); + +/** + * Copy zip archive stream output buffer. + * + * @param zip zip archive handler. + * @param buf output buffer. User should free buf. + * @param bufsize output buffer size (in bytes). + * + * @return copy size + */ +extern ZIP_EXPORT ssize_t zip_stream_copy(struct zip_t *zip, void **buf, + size_t *bufsize); + +/** + * Close zip archive releases resources. + * + * @param zip zip archive handler. + * + * @return + */ +extern ZIP_EXPORT void zip_stream_close(struct zip_t *zip); + +/** + * Creates a new archive and puts files into a single zip archive. + * + * @param zipname zip archive file. + * @param filenames input files. + * @param len: number of input files. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_create(const char *zipname, const char *filenames[], + size_t len); + +/** + * Extracts a zip archive file into directory. + * + * If on_extract_entry is not NULL, the callback will be called after + * successfully extracted each zip entry. + * Returning a negative value from the callback will cause abort and return an + * error. The last argument (void *arg) is optional, which you can use to pass + * data to the on_extract_entry callback. + * + * @param zipname zip archive file. + * @param dir output directory. + * @param on_extract_entry on extract callback. + * @param arg opaque pointer. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_extract(const char *zipname, const char *dir, + int (*on_extract_entry)(const char *filename, + void *arg), + void *arg); + +/** @} */ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/build_capy.zig b/build_capy.zig index c333b0ed..bf8002cb 100644 --- a/build_capy.zig +++ b/build_capy.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const AndroidSdk = @import("android/Sdk.zig"); //const build_zelda = @import("vendor/zelda/build.zig"); const zig_libressl = struct {}; // const zig_libressl = if (@import("builtin").os.tag == .windows) @@ -7,14 +8,43 @@ const zig_libressl = struct {}; // @import("vendor/zelda/zig-libressl/build.zig"); pub const CapyBuildOptions = struct { + android: AndroidOptions = .{}, + pub const AndroidOptions = struct { + version: AndroidSdk.AndroidVersion = .android5, + }; }; pub fn install(step: *std.build.LibExeObjStep, options: CapyBuildOptions) !void { - _ = options; const prefix = comptime std.fs.path.dirname(@src().file).? ++ std.fs.path.sep_str; step.subsystem = .Native; + const zigimg = std.build.Pkg{ + .name = "zigimg", + .source = std.build.FileSource { .path = prefix ++ "/vendor/zigimg/zigimg.zig" }, + }; + + // const zelda = build_zelda.pkgs.zelda; + // const use_system_libressl = @import("builtin").os.tag == .windows; + // if ((comptime @import("builtin").os.tag != .windows) and step.target.getOsTag() != .freestanding and step.target.getOsTag() != .windows and false) { + // try zig_libressl.useLibreSslForStep( + // step.builder, + // step.target, + // .ReleaseSafe, + // prefix ++ "/vendor/zelda/zig-libressl/libressl", + // step, + // use_system_libressl, + // ); + // } + + const capy = std.build.Pkg{ + .name = "capy", + .source = std.build.FileSource { .path = prefix ++ "/src/main.zig" }, + //.dependencies = &[_]std.build.Pkg{ zigimg, zelda }, + .dependencies = &[_]std.build.Pkg{ zigimg }, + }; + step.addPackage(capy); + switch (step.target.getOsTag()) { .windows => { switch (step.build_mode) { @@ -51,8 +81,87 @@ pub fn install(step: *std.build.LibExeObjStep, options: CapyBuildOptions) !void step.linkSystemLibraryName("objc"); }, .linux, .freebsd => { - step.linkLibC(); - step.linkSystemLibrary("gtk+-3.0"); + if (step.target.toTarget().isAndroid()) { + // TODO: automatically download the SDK and NDK and build tools? + const sdk = AndroidSdk.init(step.builder, null, .{}); + const mode = step.build_mode; + + // Provide some KeyStore structure so we can sign our app. + // Recommendation: Don't hardcore your password here, everyone can read it. + // At least not for your production keystore ;) + const key_store = AndroidSdk.KeyStore{ + .file = ".build_config/android.keystore", + .alias = "default", + .password = "ziguana", + }; + + var libraries = std.ArrayList([]const u8).init(step.builder.allocator); + try libraries.append("GLESv2"); + try libraries.append("EGL"); + try libraries.append("android"); + try libraries.append("log"); + + const config = AndroidSdk.AppConfig{ + .target_version = options.android.version, + // This is displayed to the user + .display_name = "Zig Android App Template", + // This is used internally for ... things? + .app_name = "zig-app-template", + // This is required for the APK name. This identifies your app, android will associate + // your signing key with this identifier and will prevent updates if the key changes. + .package_name = "net.random_projects.zig_android_template", + // This is a set of resources. It should at least contain a "mipmap/icon.png" resource that + // will provide the application icon. + .resources = &[_]AndroidSdk.Resource{ + .{ .path = "mipmap/icon.png", .content = .{ .path = "example/icon.png" } }, + }, + .aaudio = false, + .opensl = false, + // This is a list of android permissions. Check out the documentation to figure out which you need. + .permissions = &[_][]const u8{ + "android.permission.SET_RELEASE_APP", + //"android.permission.RECORD_AUDIO", + }, + // This is a list of native android apis to link against. + .libraries = libraries.items, + .packages = &.{ capy }, + }; + + const app = sdk.createApp( + "app-template.apk", + step.root_src.?.getPath(step.builder), + config, + mode, + .{ + .aarch64 = true, + .arm = false, + .x86_64 = false, + .x86 = false, + }, // default targets + key_store, + ); + + for (app.libraries) |exe| { + // Provide the "android" package in each executable we build + exe.addPackage(app.getAndroidPackage("android")); + } + + // Make the app build when we invoke "zig build" or "zig build install" + step.step.dependOn(app.final_step); + + //const b = step.builder; + // const keystore_step = b.step("keystore", "Initialize a fresh debug keystore"); + // const push_step = b.step("push", "Push the app to a connected android device"); + // const run_step = b.step("run", "Run the app on a connected android device"); + + // keystore_step.dependOn(sdk.initKeystore(key_store, .{})); + // push_step.dependOn(app.install()); + // run_step.dependOn(app.run()); + step.step.dependOn(app.run()); + } else { + step.linkLibC(); + step.linkSystemLibrary("gtk+-3.0"); + } }, .freestanding => { if (step.target.toTarget().isWasm()) { @@ -72,30 +181,4 @@ pub fn install(step: *std.build.LibExeObjStep, options: CapyBuildOptions) !void return error.UnsupportedOs; }, } - - const zigimg = std.build.Pkg{ - .name = "zigimg", - .source = std.build.FileSource { .path = prefix ++ "/vendor/zigimg/zigimg.zig" }, - }; - - // const zelda = build_zelda.pkgs.zelda; - // const use_system_libressl = @import("builtin").os.tag == .windows; - // if ((comptime @import("builtin").os.tag != .windows) and step.target.getOsTag() != .freestanding and step.target.getOsTag() != .windows and false) { - // try zig_libressl.useLibreSslForStep( - // step.builder, - // step.target, - // .ReleaseSafe, - // prefix ++ "/vendor/zelda/zig-libressl/libressl", - // step, - // use_system_libressl, - // ); - // } - - const capy = std.build.Pkg{ - .name = "capy", - .source = std.build.FileSource { .path = prefix ++ "/src/main.zig" }, - //.dependencies = &[_]std.build.Pkg{ zigimg, zelda }, - .dependencies = &[_]std.build.Pkg{ zigimg }, - }; - step.addPackage(capy); }